33namespace ArieTimmerman \Laravel \SCIMServer \Attribute ;
44
55use ArieTimmerman \Laravel \SCIMServer \Exceptions \SCIMException ;
6+ use ArieTimmerman \Laravel \SCIMServer \Parser \Parser ;
67use ArieTimmerman \Laravel \SCIMServer \Parser \Path ;
78use Illuminate \Database \Eloquent \Builder ;
89use Illuminate \Database \Eloquent \Model ;
910
1011class Complex extends AbstractComplex
1112{
12-
13+
1314 /**
1415 * @return string[]
1516 */
@@ -18,9 +19,9 @@ public function getSchemas()
1819 return collect ($ this ->getSchemaNodes ())->map (fn ($ element ) => $ element ->name )->values ()->toArray ();
1920 }
2021
21-
22+
2223 public function read (&$ object , array $ attributes = []): ?AttributeValue
23- {
24+ {
2425 if (!($ this instanceof Schema) && $ this ->parent != null && !empty ($ attributes ) && !in_array ($ this ->name , $ attributes ) && !in_array ($ this ->getFullKey (), $ attributes )) {
2526 return null ;
2627 }
@@ -33,8 +34,8 @@ protected function doRead(&$object, $attributes = [])
3334 {
3435 $ result = [];
3536 foreach ($ this ->subAttributes as $ attribute ) {
36- if (($ r = $ attribute ->read ($ object , $ attributes )) != null ){
37- if (config ('scim.omit_null_values ' ) && $ r ->value === null ){
37+ if (($ r = $ attribute ->read ($ object , $ attributes )) != null ) {
38+ if (config ('scim.omit_null_values ' ) && $ r ->value === null ) {
3839 continue ;
3940 }
4041 $ result [$ attribute ->name ] = $ r ->value ;
@@ -47,7 +48,7 @@ public function patch($operation, $value, Model &$object, Path $path = null, $re
4748 {
4849 $ this ->dirty = true ;
4950
50- if ($ this ->mutability == 'readOnly ' ){
51+ if ($ this ->mutability == 'readOnly ' ) {
5152 // silently ignore
5253 return ;
5354 }
@@ -112,21 +113,44 @@ public function replace($value, Model &$object, Path $path = null, $removeIfNotS
112113 $ match = false ;
113114 $ this ->dirty = true ;
114115
115- if ($ this ->mutability == 'readOnly ' ){
116+ if ($ this ->mutability == 'readOnly ' ) {
116117 // silently ignore
117118 return ;
118119 }
119120
120- // if there is no path, keys of value are attribute names
121+ // if there is no path, keys of value are attribute paths
121122 foreach ($ value as $ key => $ v ) {
122123 if (is_numeric ($ key )) {
123124 throw new SCIMException ('Invalid key: ' . $ key . ' for complex object ' . $ this ->getFullKey ());
124125 }
125126
126- $ attribute = $ this ->getSubNode ($ key );
127- if ($ attribute != null ) {
128- $ attribute ->replace ($ v , $ object , $ path );
127+ $ subNode = null ;
128+
129+ // if path contains : it is a schema node
130+ if (strpos ($ key , ': ' ) !== false ) {
131+ $ subNode = $ this ->getSubNode ($ key );
129132 $ match = true ;
133+ } else {
134+ $ path = Parser::parse ($ key );
135+
136+ if ($ path ->isNotEmpty ()) {
137+ $ attributeNames = $ path ->getAttributePathAttributes ();
138+ $ path = $ path ->shiftAttributePathAttributes ();
139+ $ sub = $ attributeNames [0 ] ?? $ path ->getAttributePath ()?->path?->schema;
140+ $ subNode = $ this ->getSubNode ($ attributeNames [0 ] ?? $ path ->getAttributePath ()?->path?->schema);
141+ $ match = true ;
142+ }
143+ }
144+
145+ if ($ match ) {
146+ $ newValue = $ v ;
147+ if ($ path ->isNotEmpty ()) {
148+ $ newValue = [
149+ implode ('. ' , $ path ->getAttributePathAttributes ()) => $ v
150+ ];
151+ }
152+
153+ $ subNode ->replace ($ newValue , $ object , $ path );
130154 }
131155 }
132156
@@ -148,10 +172,55 @@ public function replace($value, Model &$object, Path $path = null, $removeIfNotS
148172 }
149173 }
150174
175+ public function add ($ value , Model &$ object )
176+ {
177+ $ match = false ;
178+ $ this ->dirty = true ;
179+
180+ if ($ this ->mutability == 'readOnly ' ) {
181+ // silently ignore
182+ return ;
183+ }
184+
185+ // keys of value are attribute names
186+ foreach ($ value as $ key => $ v ) {
187+ if (is_numeric ($ key )) {
188+ throw new SCIMException ('Invalid key: ' . $ key . ' for complex object ' . $ this ->getFullKey ());
189+ }
190+
191+ $ path = Parser::parse ($ key );
192+
193+ if ($ path ->isNotEmpty ()) {
194+ $ attributeNames = $ path ->getAttributePathAttributes ();
195+ $ path = $ path ->shiftAttributePathAttributes ();
196+ $ subNode = $ this ->getSubNode ($ attributeNames [0 ]);
197+ $ match = true ;
198+
199+ $ newValue = $ v ;
200+ if ($ path ->isNotEmpty ()) {
201+ $ newValue = [
202+ implode ('. ' , $ path ->getAttributePathAttributes ()) => $ v
203+ ];
204+ }
205+
206+ $ subNode ->add ($ newValue , $ object );
207+ }
208+ }
209+
210+ // if this is the root, we may also check the schema nodes
211+ if (!$ match && $ this ->parent == null ) {
212+ foreach ($ this ->subAttributes as $ attribute ) {
213+ if ($ attribute instanceof Schema) {
214+ $ attribute ->add ($ value , $ object );
215+ }
216+ }
217+ }
218+ }
219+
151220
152221 public function remove ($ value , Model &$ object , string $ path = null )
153222 {
154- if ($ this ->mutability == 'readOnly ' ){
223+ if ($ this ->mutability == 'readOnly ' ) {
155224 // silently ignore
156225 return ;
157226 }
@@ -186,8 +255,8 @@ public function getSortAttributeByPath(Path $path)
186255 return $ result ;
187256 }
188257
189- public function applyComparison (Builder &$ query , Path $ path , $ parentAttribute = null ){
190-
258+ public function applyComparison (Builder &$ query , Path $ path , $ parentAttribute = null )
259+ {
191260 if ($ path != null && $ path ->isNotEmpty ()) {
192261 $ attributeNames = $ path ->getValuePathAttributes ();
193262
@@ -223,7 +292,6 @@ public function applyComparison(Builder &$query, Path $path, $parentAttribute =
223292 }
224293 }
225294 }
226-
227295 }
228296
229297 /**
0 commit comments