/** * Create a new record from the parent Model and new related records with it. * * @param array $attributes * @param array $relations * @return \Vinelab\NeoEloquent\Eloquent\Model */ public function createWith(array $attributes, array $relations) { // Collect the model attributes and label in the form of ['label' => $label, 'attributes' => $attributes] // as expected by the Query Builder. $attributes = $this->prepareForCreation($this->model, $attributes); $model = ['label' => $this->model->getTable(), 'attributes' => $attributes]; /** * Collect the related models in the following for as expected by the Query Builder: * * [ * 'label' => ['Permission'], * 'relation' => [ * 'name' => 'photos', * 'type' => 'PHOTO', * 'direction' => 'out', * ], * 'values' => [ * // A mix of models and attributes, doesn't matter really.. * ['url' => '', 'caption' => ''], * ['url' => '', 'caption' => ''] * ] * ] */ $related = []; foreach ($relations as $relation => $values) { $name = $relation; // Get the relation by calling the model's relationship function. if (!method_exists($this->model, $relation)) { throw new QueryException("The relation method {$relation}() does not exist on " . get_class($this->model)); } $relationship = $this->model->{$relation}(); // Bring the model from the relationship. $relatedModel = $relationship->getRelated(); // We will first check to see what the dev have passed as values // so that we make sure that we have an array moving forward // In the case of a model Id or an associative array or a Model instance it means that // this is probably a One-To-One relationship or the dev decided not to add // multiple records as relations so we'll wrap it up in an array. if (!is_array($values) || Helpers::isAssocArray($values) || $values instanceof Model) { $values = [$values]; } $label = $relationship->getRelated()->getTable(); $direction = $relationship->getEdgeDirection(); $type = $relationship->getRelationType(); // Hold the models that we need to attach $attach = []; // Hold the models that we need to create $create = []; // Separate the models that needs to be attached from the ones that needs // to be created. foreach ($values as $value) { // If this is a Model then the $exists property will indicate what we need // so we'll add its id to be attached. if ($value instanceof Model and $value->exists === true) { $attach[] = $value->getKey(); } elseif ($value instanceof Collection) { $attach = array_merge($attach, $value->lists('id')); } elseif (!is_array($value) && !$value instanceof Model) { $attach[] = intval($value); } else { $create[] = $this->prepareForCreation($relatedModel, $value); } } $relation = compact('name', 'type', 'direction'); $related[] = compact('relation', 'label', 'create', 'attach'); } $result = $this->query->createWith($model, $related); $models = $this->resultsToModels($this->model->getConnectionName(), $result); return !empty($models) ? reset($models) : null; }
public static function createWith(array $attributes, array $relations, array $options = []) { // we need to fire model events on all the models that are involved with our operaiton, // including the ones from the relations, starting with this model. $me = new static(); $models = [$me]; $query = static::query(); $grammar = $query->getQuery()->getGrammar(); // add parent model's mutation constraints $label = $grammar->modelAsNode($me->getDefaultNodeLabel()); $query->addManyMutation($label, $me); // setup relations foreach ($relations as $relation => $values) { $related = $me->{$relation}()->getRelated(); // if the relation holds the attributes directly instead of an array // of attributes, we transform it into an array of attributes. if (!is_array($values) || Helpers::isAssocArray($values)) { $values = [$values]; } // create instances with the related attributes so that we fire model // events on each of them. foreach ($values as $relatedModel) { // one may pass in either instances or arrays of attributes, when we get // attributes we will dynamically fill a new model instance of the related model. if (is_array($relatedModel)) { $relatedModel = $related->fill($relatedModel); } $models[] = $relatedModel; $query->addManyMutation($relation, $related); } } // fire 'creating' and 'saving' events on all models. foreach ($models as $model) { // we will fire model events on actual models, however attached models using IDs will not be considered. if ($model instanceof Model) { if ($model->fireModelEvent('creating') === false) { return false; } if ($model->fireModelEvent('saving') === false) { return false; } } } // run the query and create the records. $result = $query->createWith($attributes, $relations); // take the parent model that was created out of the results array based on // this model's label. $created = reset($result[$label]); // fire 'saved' and 'created' events on parent model. $created->finishSave($options); $created->fireModelEvent('created', false); // set related models as relations on the parent model. foreach ($relations as $method => $values) { $relation = $created->{$method}(); // is this a one-to-one relation ? If so then we add the model directly, // otherwise we create a collection of the loaded models. $related = new Collection($result[$method]); // fire model events 'created' and 'saved' on related models. $related->each(function ($model) use($options) { $model->finishSave($options); $model->fireModelEvent('created', false); }); // when the relation is 'One' instead of 'Many' we will only return the retrieved instance // instead of colletion. if ($relation instanceof OneRelation || $relation instanceof HasOne || $relation instanceof BelongsTo) { $related = $related->first(); } $created->setRelation($method, $related); } return $created; }