@@ -102,13 +102,24 @@ public function patch($operation, $value, Model &$object, Path $path = null, $re
102102 }
103103 }
104104
105- if (empty ($ matchedIndexes )) {
106- return ;
107- }
108-
109105 $ attributeNames = $ path ?->getAttributePath()?->getAttributeNames() ?? [];
110106 $ modified = false ;
111107
108+ if (empty ($ matchedIndexes )) {
109+ if ($ operation === 'add ' ) {
110+ $ newElement = $ this ->createElementFromFilter ($ filterNode , $ attributeNames , $ value );
111+
112+ if ($ newElement !== null ) {
113+ $ currentValues [] = $ newElement ;
114+ $ modified = true ;
115+ }
116+ }
117+
118+ if (!$ modified ) {
119+ return ;
120+ }
121+ }
122+
112123 foreach ($ matchedIndexes as $ index ) {
113124 if (empty ($ attributeNames )) {
114125 if ($ operation === 'remove ' ) {
@@ -638,4 +649,78 @@ private function restoreStructure(array $original, array $normalized): array
638649
639650 return $ result ;
640651 }
652+
653+ private function createElementFromFilter (AstFilter $ filter , array $ attributePath , mixed $ value ): ?array
654+ {
655+ $ base = $ this ->extractAssignmentsFromFilter ($ filter );
656+
657+ if (!empty ($ attributePath )) {
658+ $ base = $ this ->setNestedValue ($ base , $ attributePath , $ value );
659+ } elseif (is_array ($ value )) {
660+ $ base = array_replace_recursive ($ base , $ this ->normalizeElement ($ value ));
661+ } else {
662+ return null ;
663+ }
664+
665+ return $ this ->normalizeElement ($ base );
666+ }
667+
668+ private function extractAssignmentsFromFilter (AstFilter $ filter ): array
669+ {
670+ if ($ filter instanceof ComparisonExpression) {
671+ if (strtolower ($ filter ->operator ) !== 'eq ' ) {
672+ return [];
673+ }
674+
675+ $ attributeNames = $ filter ->attributePath ->getAttributeNames ();
676+
677+ if (empty ($ attributeNames )) {
678+ return [];
679+ }
680+
681+ return $ this ->setNestedValue ([], $ attributeNames , $ filter ->compareValue );
682+ }
683+
684+ if ($ filter instanceof Conjunction) {
685+ $ result = [];
686+
687+ foreach ($ filter ->getFactors () as $ factor ) {
688+ $ result = array_replace_recursive ($ result , $ this ->extractAssignmentsFromFilter ($ factor ));
689+ }
690+
691+ return $ result ;
692+ }
693+
694+ if ($ filter instanceof AstValuePath) {
695+ $ nested = $ this ->extractAssignmentsFromFilter ($ filter ->getFilter ());
696+
697+ $ attributeNames = $ filter ->getAttributePath ()->getAttributeNames ();
698+
699+ return $ this ->setNestedValue ([], $ attributeNames , $ nested );
700+ }
701+
702+ return [];
703+ }
704+
705+ private function setNestedValue (array $ array , array $ path , mixed $ value ): array
706+ {
707+ if (empty ($ path )) {
708+ return is_array ($ value ) ? array_replace_recursive ($ array , $ value ) : $ array ;
709+ }
710+
711+ $ segment = array_shift ($ path );
712+
713+ if (!array_key_exists ($ segment , $ array ) || !is_array ($ array [$ segment ])) {
714+ $ array [$ segment ] = [];
715+ }
716+
717+ if (empty ($ path )) {
718+ $ array [$ segment ] = $ value ;
719+ return $ array ;
720+ }
721+
722+ $ array [$ segment ] = $ this ->setNestedValue ($ array [$ segment ], $ path , $ value );
723+
724+ return $ array ;
725+ }
641726}
0 commit comments