1+ <?php
2+
3+ namespace Movor \LaravelCustomCasts ;
4+
5+ trait CustomCastableTrait
6+ {
7+ /**
8+ * Each field which is going to be custom casted
9+ * will have its own custom cast instance in this array
10+ *
11+ * @var array
12+ */
13+ protected $ customCastObjects = [];
14+
15+ /**
16+ * Boot trait
17+ */
18+ public static function bootCustomCastableTrait ()
19+ {
20+ // Enable custom cast classes to listen to model events
21+ \Event::listen ('eloquent.*: ' . get_called_class (), function ($ event , $ data ) {
22+ $ eventName = explode ('. ' , explode (': ' , $ event )[0 ])[1 ];
23+
24+ /** @var self $model */
25+ $ model = $ data [0 ];
26+
27+ if (!isset ($ model ->customCasts )) {
28+ self ::throwInvalidCustomCastException ($ model );
29+ }
30+
31+ foreach ($ model ->customCasts as $ key => $ value ) {
32+ $ customCastObject = $ model ->getCustomCastObject ($ key );
33+
34+ if (method_exists ($ customCastObject , $ eventName )) {
35+ $ customCastObject ->$ eventName ();
36+ }
37+ }
38+ });
39+ }
40+
41+ /**
42+ * Hook into setAttribute logic and enable our custom cast do the job.
43+ *
44+ * This method is will override method in HasAttributes trait.
45+ *
46+ * @param $attribute
47+ * @param $value
48+ *
49+ * @throws \Exception
50+ *
51+ * @return mixed
52+ */
53+ public function setAttribute ($ attribute , $ value )
54+ {
55+ if (// Handle defined mutators in object and
56+ // prioritize them against custom castable
57+ !$ this ->hasSetMutator ($ attribute ) &&
58+
59+ // Check if there is custom casts for this attribute
60+ isset ($ this ->customCasts ) && array_key_exists ($ attribute , $ this ->customCasts )
61+ ) {
62+ $ customCastObject = $ this ->getCustomCastObject ($ attribute );
63+
64+ // Cast attribute according to logic from custom cast object
65+ $ this ->attributes [$ attribute ] = $ customCastObject ->castAttribute ($ value );
66+
67+ return $ this ;
68+ }
69+
70+ return parent ::setAttribute ($ attribute , $ value );
71+ }
72+
73+ /**
74+ * Lazy load custom cast object and return it
75+ *
76+ * @param $attribute
77+ *
78+ * @throws \Exception
79+ *
80+ * @return CustomCastableBase
81+ */
82+ protected function getCustomCastObject ($ attribute )
83+ {
84+ // Check if custom cast object already been set
85+ if (isset ($ this ->customCastObjects [$ attribute ])) {
86+ return $ this ->customCastObjects [$ attribute ];
87+ }
88+
89+ // Check if custom casts property exists for this attribute
90+ if (isset ($ this ->customCasts ) && array_key_exists ($ attribute , $ this ->customCasts )) {
91+ $ customCastClass = $ this ->customCasts [$ attribute ];
92+
93+ // Make sure that defined custom cast class is correct
94+ if (!is_subclass_of ($ customCastClass , CustomCastableBase::class)) {
95+ $ message = "Custom cast class for ' $ attribute' needs to be extended from " ;
96+ $ message .= CustomCastableBase::class;
97+
98+ throw new \Exception ($ message );
99+ }
100+
101+ $ customCastObject = new $ customCastClass ($ this , $ attribute );
102+ return $ this ->customCastObjects [$ attribute ] = $ customCastObject ;
103+ }
104+
105+ self ::throwInvalidCustomCastException ($ this );
106+ }
107+
108+ /**
109+ * @param $model
110+ *
111+ * @throws \Exception
112+ */
113+ public static function throwInvalidCustomCastException ($ model )
114+ {
115+ $ trait = CustomCastableTrait::class;
116+ $ model = self ::class;
117+ $ message = "Model class ' $ model' which uses ' $ trait' needs to have 'customCast' array property defined " ;
118+
119+ throw new \Exception ($ message );
120+ }
121+ }
0 commit comments