timestamps = $instance->hasTimestampAttributes($attributes); // The pivot model is a "dynamic" model since we will set the tables dynamically // for the instance. This allows it work for any intermediate tables for the // many to many relationship that are defined by this developer's classes. $instance->setConnection($parent->getConnectionName()) ->setTable($table) ->forceFill($attributes) ->syncOriginal(); // We store off the parent instance so we will access the timestamp column names // for the model, since the pivot model timestamps aren't easily configurable // from the developer's point of view. We can use the parents to get these. $instance->pivotParent = $parent; $instance->exists = $exists; return $instance; } /** * Create a new pivot model from raw values returned from a query. * * @param \Illuminate\Database\Eloquent\Model $parent * @param array $attributes * @param string $table * @param bool $exists * @return static */ public static function fromRawAttributes(Model $parent, $attributes, $table, $exists = false) { $instance = static::fromAttributes($parent, [], $table, $exists); $instance->timestamps = $instance->hasTimestampAttributes($attributes); $instance->setRawAttributes( array_merge($instance->getRawOriginal(), $attributes), $exists ); return $instance; } /** * Set the keys for a select query. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ protected function setKeysForSelectQuery($query) { if (isset($this->attributes[$this->getKeyName()])) { return parent::setKeysForSelectQuery($query); } $query->where($this->foreignKey, $this->getOriginal( $this->foreignKey, $this->getAttribute($this->foreignKey) )); return $query->where($this->relatedKey, $this->getOriginal( $this->relatedKey, $this->getAttribute($this->relatedKey) )); } /** * Set the keys for a save update query. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ protected function setKeysForSaveQuery($query) { return $this->setKeysForSelectQuery($query); } /** * Delete the pivot model record from the database. * * @return int */ public function delete() { if (isset($this->attributes[$this->getKeyName()])) { return (int) parent::delete(); } if ($this->fireModelEvent('deleting') === false) { return 0; } $this->touchOwners(); return tap($this->getDeleteQuery()->delete(), function () { $this->exists = false; $this->fireModelEvent('deleted', false); }); } /** * Get the query builder for a delete operation on the pivot. * * @return \Illuminate\Database\Eloquent\Builder */ protected function getDeleteQuery() { return $this->newQueryWithoutRelationships()->where([ $this->foreignKey => $this->getOriginal($this->foreignKey, $this->getAttribute($this->foreignKey)), $this->relatedKey => $this->getOriginal($this->relatedKey, $this->getAttribute($this->relatedKey)), ]); } /** * Get the table associated with the model. * * @return string */ public function getTable() { if (! isset($this->table)) { $this->setTable(str_replace( '\\', '', Str::snake(Str::singular(class_basename($this))) )); } return $this->table; } /** * Get the foreign key column name. * * @return string */ public function getForeignKey() { return $this->foreignKey; } /** * Get the "related key" column name. * * @return string */ public function getRelatedKey() { return $this->relatedKey; } /** * Get the "related key" column name. * * @return string */ public function getOtherKey() { return $this->getRelatedKey(); } /** * Set the key names for the pivot model instance. * * @param string $foreignKey * @param string $relatedKey * @return $this */ public function setPivotKeys($foreignKey, $relatedKey) { $this->foreignKey = $foreignKey; $this->relatedKey = $relatedKey; return $this; } /** * Determine if the pivot model or given attributes has timestamp attributes. * * @param array|null $attributes * @return bool */ public function hasTimestampAttributes($attributes = null) { return array_key_exists($this->getCreatedAtColumn(), $attributes ?? $this->attributes); } /** * Get the name of the "created at" column. * * @return string */ public function getCreatedAtColumn() { return $this->pivotParent ? $this->pivotParent->getCreatedAtColumn() : parent::getCreatedAtColumn(); } /** * Get the name of the "updated at" column. * * @return string */ public function getUpdatedAtColumn() { return $this->pivotParent ? $this->pivotParent->getUpdatedAtColumn() : parent::getUpdatedAtColumn(); } /** * Get the queueable identity for the entity. * * @return mixed */ public function getQueueableId() { if (isset($this->attributes[$this->getKeyName()])) { return $this->getKey(); } return sprintf( '%s:%s:%s:%s', $this->foreignKey, $this->getAttribute($this->foreignKey), $this->relatedKey, $this->getAttribute($this->relatedKey) ); } /** * Get a new query to restore one or more models by their queueable IDs. * * @param int[]|string[]|string $ids * @return \Illuminate\Database\Eloquent\Builder */ public function newQueryForRestoration($ids) { if (is_array($ids)) { return $this->newQueryForCollectionRestoration($ids); } if (! str_contains($ids, ':')) { return parent::newQueryForRestoration($ids); } $segments = explode(':', $ids); return $this->newQueryWithoutScopes() ->where($segments[0], $segments[1]) ->where($segments[2], $segments[3]); } /** * Get a new query to restore multiple models by their queueable IDs. * * @param int[]|string[] $ids * @return \Illuminate\Database\Eloquent\Builder */ protected function newQueryForCollectionRestoration(array $ids) { $ids = array_values($ids); if (! str_contains($ids[0], ':')) { return parent::newQueryForRestoration($ids); } $query = $this->newQueryWithoutScopes(); foreach ($ids as $id) { $segments = explode(':', $id); $query->orWhere(function ($query) use ($segments) { return $query->where($segments[0], $segments[1]) ->where($segments[2], $segments[3]); }); } return $query; } /** * Unset all the loaded relations for the instance. * * @return $this */ public function unsetRelations() { $this->pivotParent = null; $this->relations = []; return $this; } }