Exemplo n.º 1
0
 /**
  * Perform a deserialization
  *
  * @param array|Closure $serializationProvider An anonymized serialization by array or generator
  * @return array An array consisting of the encountered internal ids as keys, and created db ids as values
  * @throws Exception on invalid rules
  */
 public function deserialize($serializationProvider)
 {
     if (is_array($serializationProvider)) {
         // Pretend the array is an generator
         $generator = function () use($serializationProvider) {
             return [$serializationProvider];
         };
     } elseif ($serializationProvider instanceof Closure) {
         // Assume the given closure is a generator
         $generator = $serializationProvider;
     } else {
         throw new Exception("Provided serialization provider must be either an array or a generator");
     }
     // Crank out the serialized data
     foreach ($generator() as $serialization) {
         // Count entities for simple progress functionality
         $entityCount = 0;
         foreach ($serialization as $items) {
             $entityCount += count($items);
         }
         // Set the goal to include all entities
         $this->state->setProgressGoal($entityCount);
         // Iterate through the serialization data Array<FQCN, Array<Serialized entity>>
         foreach ($serialization as $fqcn => $serializedEntities) {
             $this->state->push($fqcn);
             // Debug
             // Iterate through the serialized entities
             foreach ($serializedEntities as $serializedEntity) {
                 $model = $this->app->make($fqcn);
                 // Get the internal id of this entity
                 $id = $serializedEntity["@id"];
                 // Has primary key in data?
                 if (isset($serializedEntity[$model->getKeyName()])) {
                     // Get database id
                     $dbId = $serializedEntity[$model->getKeyName()];
                     // Set manually
                     $model->id = $dbId;
                     $model->exists = true;
                 }
                 // Get blueprints for this model
                 $blueprint = $this->getBlueprint($model, $serializedEntity);
                 // If blueprint is false, we don't want to deserialize at all
                 if ($blueprint === false) {
                     continue;
                 }
                 foreach ($blueprint as $rule) {
                     if (is_array($rule)) {
                         $field = $rule[0];
                     } else {
                         $field = $rule;
                     }
                     $this->state->push($field);
                     if (!method_exists($model, $field)) {
                         // Non-relation
                         // If the rule is an array, it's supposed contain a set of rules
                         if (is_array($rule) && is_array($serializedEntity[$field])) {
                             // Match rules for this field found, figure out how to use them
                             if (count($rule) === 2 && is_array($serializedEntity[$field])) {
                                 // Rules
                                 // $rule[0] = field name
                                 // $rule[1] = array of rules
                                 // serialized content = array
                                 $model->{$field} = $this->toDbIds($model, $id, $field, $serializedEntity[$field], $rule[1]);
                             } elseif (count($rule) === 3) {
                                 // Conditional Rules
                                 // $rule[0] = field name
                                 // $rule[1] = field name to match condition against
                                 // $rule[2] = associative array where key is the value to match the field against,
                                 //            and the value is the array of rules to use when a match is found
                                 $matchAgainst = $serializedEntity[$rule[1]];
                                 if (isset($rule[2][$matchAgainst])) {
                                     $model->{$field} = $this->toDbIds($model, $id, $field, $serializedEntity[$field], $rule[2][$matchAgainst]);
                                 } else {
                                     $model->{$field} = $serializedEntity[$field];
                                 }
                             } else {
                                 throw new Exception("Unsupported rule with weird array count of " . count($rule));
                             }
                         } else {
                             // The rule or content weren't arrays, so we just want to save the content
                             $model->{$field} = $serializedEntity[$field];
                         }
                     } else {
                         // The field is a relation
                         $relation = $model->{$field}();
                         $relationName = last(explode("\\", get_class($relation)));
                         switch ($relationName) {
                             case "BelongsTo":
                                 // Don't associate nulls
                                 if (isset($serializedEntity[$field])) {
                                     // Associate or defer an association
                                     $this->bookKeeper->associate($model, $field, $id, $serializedEntity[$field]);
                                 }
                                 break;
                             case "MorphTo":
                                 // Morph or defer a morph
                                 $this->bookKeeper->morph($model, $field, $id, $serializedEntity[$field]);
                                 break;
                         }
                     }
                     $this->state->pop();
                     // Debug
                 }
                 // Handle progress
                 $this->state->incrementProgress();
                 // Create/Update the database entity
                 $model->save();
                 // Report the real db id
                 $this->bookKeeper->bind($id, $model->getKey());
             }
             $this->state->pop();
             // Debug
         }
     }
     // Resolve all deferred actions and return the book keeping Array<Internal id, Database id>
     return $this->bookKeeper->resolve();
 }