Вход Регистрация
Файл: src/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php
Строк: 1264
<?php

namespace IlluminateDatabaseEloquentRelations;

use 
IlluminateSupportArr;
use 
IlluminateSupportStr;
use 
IlluminateDatabaseEloquentModel;
use 
IlluminateDatabaseEloquentBuilder;
use 
IlluminateDatabaseEloquentCollection;
use 
IlluminateDatabaseEloquentModelNotFoundException;

class 
BelongsToMany extends Relation
{
    
/**
     * The intermediate table for the relation.
     *
     * @var string
     */
    
protected $table;

    
/**
     * The foreign key of the parent model.
     *
     * @var string
     */
    
protected $foreignKey;

    
/**
     * The associated key of the relation.
     *
     * @var string
     */
    
protected $otherKey;

    
/**
     * The "name" of the relationship.
     *
     * @var string
     */
    
protected $relationName;

    
/**
     * The pivot table columns to retrieve.
     *
     * @var array
     */
    
protected $pivotColumns = [];

    
/**
     * Any pivot table restrictions.
     *
     * @var array
     */
    
protected $pivotWheres = [];

    
/**
     * The custom pivot table column for the created_at timestamp.
     *
     * @var string
     */
    
protected $pivotCreatedAt;

    
/**
     * The custom pivot table column for the updated_at timestamp.
     *
     * @var string
     */
    
protected $pivotUpdatedAt;

    
/**
     * Create a new belongs to many relationship instance.
     *
     * @param  IlluminateDatabaseEloquentBuilder  $query
     * @param  IlluminateDatabaseEloquentModel  $parent
     * @param  string  $table
     * @param  string  $foreignKey
     * @param  string  $otherKey
     * @param  string  $relationName
     * @return void
     */
    
public function __construct(Builder $query, Model $parent, $table, $foreignKey, $otherKey, $relationName = null)
    {
        
$this->table = $table;
        
$this->otherKey = $otherKey;
        
$this->foreignKey = $foreignKey;
        
$this->relationName = $relationName;

        
parent::__construct($query, $parent);
    }

    
/**
     * Get the results of the relationship.
     *
     * @return mixed
     */
    
public function getResults()
    {
        return 
$this->get();
    }

    
/**
     * Set a where clause for a pivot table column.
     *
     * @param  string  $column
     * @param  string  $operator
     * @param  mixed   $value
     * @param  string  $boolean
     * @return IlluminateDatabaseEloquentRelationsBelongsToMany
     */
    
public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
    {
        
$this->pivotWheres[] = func_get_args();

        return 
$this->where($this->table.'.'.$column, $operator, $value, $boolean);
    }

    
/**
     * Set an or where clause for a pivot table column.
     *
     * @param  string  $column
     * @param  string  $operator
     * @param  mixed   $value
     * @return IlluminateDatabaseEloquentRelationsBelongsToMany
     */
    
public function orWherePivot($column, $operator = null, $value = null)
    {
        return 
$this->wherePivot($column, $operator, $value, 'or');
    }

    
/**
     * Execute the query and get the first result.
     *
     * @param  array   $columns
     * @return mixed
     */
    
public function first($columns = ['*'])
    {
        
$results = $this->take(1)->get($columns);

        return 
count($results) > 0 ? $results->first() : null;
    }

    
/**
     * Execute the query and get the first result or throw an exception.
     *
     * @param  array  $columns
     * @return IlluminateDatabaseEloquentModel|static
     *
     * @throws IlluminateDatabaseEloquentModelNotFoundException
     */
    
public function firstOrFail($columns = ['*'])
    {
        if (! 
is_null($model = $this->first($columns))) {
            return 
$model;
        }

        throw new 
ModelNotFoundException;
    }

    
/**
     * Execute the query as a "select" statement.
     *
     * @param  array  $columns
     * @return IlluminateDatabaseEloquentCollection
     */
    
public function get($columns = ['*'])
    {
        
// First we'll add the proper select columns onto the query so it is run with
        // the proper columns. Then, we will get the results and hydrate out pivot
        // models with the result of those columns as a separate model relation.
        
$columns = $this->query->getQuery()->columns ? [] : $columns;

        
$select = $this->getSelectColumns($columns);

        
$builder = $this->query->applyScopes();

        
$models = $builder->addSelect($select)->getModels();

        
$this->hydratePivotRelation($models);

        
// If we actually found models we will also eager load any relationships that
        // have been specified as needing to be eager loaded. This will solve the
        // n + 1 query problem for the developer and also increase performance.
        
if (count($models) > 0) {
            
$models = $builder->eagerLoadRelations($models);
        }

        return 
$this->related->newCollection($models);
    }

    
/**
     * Get a paginator for the "select" statement.
     *
     * @param  int  $perPage
     * @param  array  $columns
     * @param  string  $pageName
     * @param  int|null  $page
     * @return IlluminateContractsPaginationLengthAwarePaginator
     */
    
public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
    {
        
$this->query->addSelect($this->getSelectColumns($columns));

        
$paginator = $this->query->paginate($perPage, $columns, $pageName, $page);

        
$this->hydratePivotRelation($paginator->items());

        return 
$paginator;
    }

    
/**
     * Paginate the given query into a simple paginator.
     *
     * @param  int  $perPage
     * @param  array  $columns
     * @param  string  $pageName
     * @return IlluminateContractsPaginationPaginator
     */
    
public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page')
    {
        
$this->query->addSelect($this->getSelectColumns($columns));

        
$paginator = $this->query->simplePaginate($perPage, $columns, $pageName);

        
$this->hydratePivotRelation($paginator->items());

        return 
$paginator;
    }

    
/**
     * Chunk the results of the query.
     *
     * @param  int  $count
     * @param  callable  $callback
     * @return bool
     */
    
public function chunk($count, callable $callback)
    {
        
$this->query->addSelect($this->getSelectColumns());

        return 
$this->query->chunk($count, function ($results) use ($callback) {
            
$this->hydratePivotRelation($results->all());

            return 
$callback($results);
        });
    }

    
/**
     * Hydrate the pivot table relationship on the models.
     *
     * @param  array  $models
     * @return void
     */
    
protected function hydratePivotRelation(array $models)
    {
        
// To hydrate the pivot relationship, we will just gather the pivot attributes
        // and create a new Pivot model, which is basically a dynamic model that we
        // will set the attributes, table, and connections on so it they be used.
        
foreach ($models as $model) {
            
$pivot = $this->newExistingPivot($this->cleanPivotAttributes($model));

            
$model->setRelation('pivot', $pivot);
        }
    }

    
/**
     * Get the pivot attributes from a model.
     *
     * @param  IlluminateDatabaseEloquentModel  $model
     * @return array
     */
    
protected function cleanPivotAttributes(Model $model)
    {
        
$values = [];

        foreach (
$model->getAttributes() as $key => $value) {
            
// To get the pivots attributes we will just take any of the attributes which
            // begin with "pivot_" and add those to this arrays, as well as unsetting
            // them from the parent's models since they exist in a different table.
            
if (strpos($key, 'pivot_') === 0) {
                
$values[substr($key, 6)] = $value;

                unset(
$model->$key);
            }
        }

        return 
$values;
    }

    
/**
     * Set the base constraints on the relation query.
     *
     * @return void
     */
    
public function addConstraints()
    {
        
$this->setJoin();

        if (static::
$constraints) {
            
$this->setWhere();
        }
    }

    
/**
     * Add the constraints for a relationship query.
     *
     * @param  IlluminateDatabaseEloquentBuilder  $query
     * @param  IlluminateDatabaseEloquentBuilder  $parent
     * @param  array|mixed  $columns
     * @return IlluminateDatabaseEloquentBuilder
     */
    
public function getRelationQuery(Builder $query, Builder $parent, $columns = ['*'])
    {
        if (
$parent->getQuery()->from == $query->getQuery()->from) {
            return 
$this->getRelationQueryForSelfJoin($query, $parent, $columns);
        }

        
$this->setJoin($query);

        return 
parent::getRelationQuery($query, $parent, $columns);
    }

    
/**
     * Add the constraints for a relationship query on the same table.
     *
     * @param  IlluminateDatabaseEloquentBuilder  $query
     * @param  IlluminateDatabaseEloquentBuilder  $parent
     * @param  array|mixed  $columns
     * @return IlluminateDatabaseEloquentBuilder
     */
    
public function getRelationQueryForSelfJoin(Builder $query, Builder $parent, $columns = ['*'])
    {
        
$query->select($columns);

        
$query->from($this->related->getTable().' as '.$hash = $this->getRelationCountHash());

        
$this->related->setTable($hash);

        
$this->setJoin($query);

        return 
parent::getRelationQuery($query, $parent, $columns);
    }

    
/**
     * Get a relationship join table hash.
     *
     * @return string
     */
    
public function getRelationCountHash()
    {
        return 
'self_'.md5(microtime(true));
    }

    
/**
     * Set the select clause for the relation query.
     *
     * @param  array  $columns
     * @return IlluminateDatabaseEloquentRelationsBelongsToMany
     */
    
protected function getSelectColumns(array $columns = ['*'])
    {
        if (
$columns == ['*']) {
            
$columns = [$this->related->getTable().'.*'];
        }

        return 
array_merge($columns, $this->getAliasedPivotColumns());
    }

    
/**
     * Get the pivot columns for the relation.
     *
     * @return array
     */
    
protected function getAliasedPivotColumns()
    {
        
$defaults = [$this->foreignKey, $this->otherKey];

        
// We need to alias all of the pivot columns with the "pivot_" prefix so we
        // can easily extract them out of the models and put them into the pivot
        // relationships when they are retrieved and hydrated into the models.
        
$columns = [];

        foreach (
array_merge($defaults, $this->pivotColumns) as $column) {
            
$columns[] = $this->table.'.'.$column.' as pivot_'.$column;
        }

        return 
array_unique($columns);
    }

    
/**
     * Determine whether the given column is defined as a pivot column.
     *
     * @param  string  $column
     * @return bool
     */
    
protected function hasPivotColumn($column)
    {
        return 
in_array($column, $this->pivotColumns);
    }

    
/**
     * Set the join clause for the relation query.
     *
     * @param  IlluminateDatabaseEloquentBuilder|null  $query
     * @return $this
     */
    
protected function setJoin($query = null)
    {
        
$query = $query ?: $this->query;

        
// We need to join to the intermediate table on the related model's primary
        // key column with the intermediate table's foreign key for the related
        // model instance. Then we can set the "where" for the parent models.
        
$baseTable = $this->related->getTable();

        
$key = $baseTable.'.'.$this->related->getKeyName();

        
$query->join($this->table, $key, '=', $this->getOtherKey());

        return 
$this;
    }

    
/**
     * Set the where clause for the relation query.
     *
     * @return $this
     */
    
protected function setWhere()
    {
        
$foreign = $this->getForeignKey();

        
$this->query->where($foreign, '=', $this->parent->getKey());

        return 
$this;
    }

    
/**
     * Set the constraints for an eager load of the relation.
     *
     * @param  array  $models
     * @return void
     */
    
public function addEagerConstraints(array $models)
    {
        
$this->query->whereIn($this->getForeignKey(), $this->getKeys($models));
    }

    
/**
     * Initialize the relation on a set of models.
     *
     * @param  array   $models
     * @param  string  $relation
     * @return array
     */
    
public function initRelation(array $models, $relation)
    {
        foreach (
$models as $model) {
            
$model->setRelation($relation, $this->related->newCollection());
        }

        return 
$models;
    }

    
/**
     * Match the eagerly loaded results to their parents.
     *
     * @param  array   $models
     * @param  IlluminateDatabaseEloquentCollection  $results
     * @param  string  $relation
     * @return array
     */
    
public function match(array $models, Collection $results, $relation)
    {
        
$dictionary = $this->buildDictionary($results);

        
// Once we have an array dictionary of child objects we can easily match the
        // children back to their parent using the dictionary and the keys on the
        // the parent models. Then we will return the hydrated models back out.
        
foreach ($models as $model) {
            if (isset(
$dictionary[$key = $model->getKey()])) {
                
$collection = $this->related->newCollection($dictionary[$key]);

                
$model->setRelation($relation, $collection);
            }
        }

        return 
$models;
    }

    
/**
     * Build model dictionary keyed by the relation's foreign key.
     *
     * @param  IlluminateDatabaseEloquentCollection  $results
     * @return array
     */
    
protected function buildDictionary(Collection $results)
    {
        
$foreign = $this->foreignKey;

        
// First we will build a dictionary of child models keyed by the foreign key
        // of the relation so that we will easily and quickly match them to their
        // parents without having a possibly slow inner loops for every models.
        
$dictionary = [];

        foreach (
$results as $result) {
            
$dictionary[$result->pivot->$foreign][] = $result;
        }

        return 
$dictionary;
    }

    
/**
     * Touch all of the related models for the relationship.
     *
     * E.g.: Touch all roles associated with this user.
     *
     * @return void
     */
    
public function touch()
    {
        
$key = $this->getRelated()->getKeyName();

        
$columns = $this->getRelatedFreshUpdate();

        
// If we actually have IDs for the relation, we will run the query to update all
        // the related model's timestamps, to make sure these all reflect the changes
        // to the parent models. This will help us keep any caching synced up here.
        
$ids = $this->getRelatedIds();

        if (
count($ids) > 0) {
            
$this->getRelated()->newQuery()->whereIn($key, $ids)->update($columns);
        }
    }

    
/**
     * Get all of the IDs for the related models.
     *
     * @return IlluminateSupportCollection
     */
    
public function getRelatedIds()
    {
        
$related = $this->getRelated();

        
$fullKey = $related->getQualifiedKeyName();

        return 
$this->getQuery()->select($fullKey)->pluck($related->getKeyName());
    }

    
/**
     * Save a new model and attach it to the parent model.
     *
     * @param  IlluminateDatabaseEloquentModel  $model
     * @param  array  $joining
     * @param  bool   $touch
     * @return IlluminateDatabaseEloquentModel
     */
    
public function save(Model $model, array $joining = [], $touch = true)
    {
        
$model->save(['touch' => false]);

        
$this->attach($model->getKey(), $joining, $touch);

        return 
$model;
    }

    
/**
     * Save an array of new models and attach them to the parent model.
     *
     * @param  IlluminateSupportCollection|array  $models
     * @param  array  $joinings
     * @return array
     */
    
public function saveMany($models, array $joinings = [])
    {
        foreach (
$models as $key => $model) {
            
$this->save($model, (array) Arr::get($joinings, $key), false);
        }

        
$this->touchIfTouching();

        return 
$models;
    }

    
/**
     * Find a related model by its primary key.
     *
     * @param  mixed  $id
     * @param  array  $columns
     * @return IlluminateDatabaseEloquentModel|IlluminateDatabaseEloquentCollection|null
     */
    
public function find($id, $columns = ['*'])
    {
        if (
is_array($id)) {
            return 
$this->findMany($id, $columns);
        }

        
$this->where($this->getRelated()->getQualifiedKeyName(), '=', $id);

        return 
$this->first($columns);
    }

    
/**
     * Find multiple related models by their primary keys.
     *
     * @param  mixed  $ids
     * @param  array  $columns
     * @return IlluminateDatabaseEloquentCollection
     */
    
public function findMany($ids, $columns = ['*'])
    {
        if (empty(
$ids)) {
            return 
$this->getRelated()->newCollection();
        }

        
$this->whereIn($this->getRelated()->getQualifiedKeyName(), $ids);

        return 
$this->get($columns);
    }

    
/**
     * Find a related model by its primary key or throw an exception.
     *
     * @param  mixed  $id
     * @param  array  $columns
     * @return IlluminateDatabaseEloquentModel|IlluminateDatabaseEloquentCollection
     *
     * @throws IlluminateDatabaseEloquentModelNotFoundException
     */
    
public function findOrFail($id, $columns = ['*'])
    {
        
$result = $this->find($id, $columns);

        if (
is_array($id)) {
            if (
count($result) == count(array_unique($id))) {
                return 
$result;
            }
        } elseif (! 
is_null($result)) {
            return 
$result;
        }

        throw (new 
ModelNotFoundException)->setModel(get_class($this->parent));
    }

    
/**
     * Find a related model by its primary key or return new instance of the related model.
     *
     * @param  mixed  $id
     * @param  array  $columns
     * @return IlluminateSupportCollection|IlluminateDatabaseEloquentModel
     */
    
public function findOrNew($id, $columns = ['*'])
    {
        if (
is_null($instance = $this->find($id, $columns))) {
            
$instance = $this->getRelated()->newInstance();
        }

        return 
$instance;
    }

    
/**
     * Get the first related model record matching the attributes or instantiate it.
     *
     * @param  array  $attributes
     * @return IlluminateDatabaseEloquentModel
     */
    
public function firstOrNew(array $attributes)
    {
        if (
is_null($instance = $this->where($attributes)->first())) {
            
$instance = $this->related->newInstance($attributes);
        }

        return 
$instance;
    }

    
/**
     * Get the first related record matching the attributes or create it.
     *
     * @param  array  $attributes
     * @param  array  $joining
     * @param  bool   $touch
     * @return IlluminateDatabaseEloquentModel
     */
    
public function firstOrCreate(array $attributes, array $joining = [], $touch = true)
    {
        if (
is_null($instance = $this->where($attributes)->first())) {
            
$instance = $this->create($attributes, $joining, $touch);
        }

        return 
$instance;
    }

    
/**
     * Create or update a related record matching the attributes, and fill it with values.
     *
     * @param  array  $attributes
     * @param  array  $values
     * @param  array  $joining
     * @param  bool   $touch
     * @return IlluminateDatabaseEloquentModel
     */
    
public function updateOrCreate(array $attributes, array $values = [], array $joining = [], $touch = true)
    {
        if (
is_null($instance = $this->where($attributes)->first())) {
            return 
$this->create($values, $joining, $touch);
        }

        
$instance->fill($values);

        
$instance->save(['touch' => false]);

        return 
$instance;
    }

    
/**
     * Create a new instance of the related model.
     *
     * @param  array  $attributes
     * @param  array  $joining
     * @param  bool   $touch
     * @return IlluminateDatabaseEloquentModel
     */
    
public function create(array $attributes, array $joining = [], $touch = true)
    {
        
$instance = $this->related->newInstance($attributes);

        
// Once we save the related model, we need to attach it to the base model via
        // through intermediate table so we'll use the existing "attach" method to
        // accomplish this which will insert the record and any more attributes.
        
$instance->save(['touch' => false]);

        
$this->attach($instance->getKey(), $joining, $touch);

        return 
$instance;
    }

    
/**
     * Create an array of new instances of the related models.
     *
     * @param  array  $records
     * @param  array  $joinings
     * @return array
     */
    
public function createMany(array $records, array $joinings = [])
    {
        
$instances = [];

        foreach (
$records as $key => $record) {
            
$instances[] = $this->create($record, (array) Arr::get($joinings, $key), false);
        }

        
$this->touchIfTouching();

        return 
$instances;
    }

    
/**
     * Sync the intermediate tables with a list of IDs or collection of models.
     *
     * @param  IlluminateDatabaseEloquentCollection|array  $ids
     * @param  bool   $detaching
     * @return array
     */
    
public function sync($ids, $detaching = true)
    {
        
$changes = [
            
'attached' => [], 'detached' => [], 'updated' => [],
        ];

        if (
$ids instanceof Collection) {
            
$ids = $ids->modelKeys();
        }

        
// First we need to attach any of the associated models that are not currently
        // in this joining table. We'll spin through the given IDs, checking to see
        // if they exist in the array of current ones, and if not we will insert.
        
$current = $this->newPivotQuery()->pluck($this->otherKey);

        
$records = $this->formatSyncList($ids);

        
$detach = array_diff($current, array_keys($records));

        
// Next, we will take the differences of the currents and given IDs and detach
        // all of the entities that exist in the "current" array but are not in the
        // the array of the IDs given to the method which will complete the sync.
        
if ($detaching && count($detach) > 0) {
            
$this->detach($detach);

            
$changes['detached'] = (array) array_map(function ($v) {
                return 
is_numeric($v) ? (int) $v : (string) $v;
            }, 
$detach);
        }

        
// Now we are finally ready to attach the new records. Note that we'll disable
        // touching until after the entire operation is complete so we don't fire a
        // ton of touch operations until we are totally done syncing the records.
        
$changes = array_merge(
            
$changes, $this->attachNew($records, $current, false)
        );

        if (
count($changes['attached']) || count($changes['updated'])) {
            
$this->touchIfTouching();
        }

        return 
$changes;
    }

    
/**
     * Format the sync list so that it is keyed by ID.
     *
     * @param  array  $records
     * @return array
     */
    
protected function formatSyncList(array $records)
    {
        
$results = [];

        foreach (
$records as $id => $attributes) {
            if (! 
is_array($attributes)) {
                list(
$id, $attributes) = [$attributes, []];
            }

            
$results[$id] = $attributes;
        }

        return 
$results;
    }

    
/**
     * Attach all of the IDs that aren't in the current array.
     *
     * @param  array  $records
     * @param  array  $current
     * @param  bool   $touch
     * @return array
     */
    
protected function attachNew(array $records, array $current, $touch = true)
    {
        
$changes = ['attached' => [], 'updated' => []];

        foreach (
$records as $id => $attributes) {
            
// If the ID is not in the list of existing pivot IDs, we will insert a new pivot
            // record, otherwise, we will just update this existing record on this joining
            // table, so that the developers will easily update these records pain free.
            
if (! in_array($id, $current)) {
                
$this->attach($id, $attributes, $touch);

                
$changes['attached'][] = is_numeric($id) ? (int) $id : (string) $id;
            }

            
// Now we'll try to update an existing pivot record with the attributes that were
            // given to the method. If the model is actually updated we will add it to the
            // list of updated pivot records so we return them back out to the consumer.
            
elseif (count($attributes) > 0 &&
                
$this->updateExistingPivot($id, $attributes, $touch)) {
                
$changes['updated'][] = is_numeric($id) ? (int) $id : (string) $id;
            }
        }

        return 
$changes;
    }

    
/**
     * Update an existing pivot record on the table.
     *
     * @param  mixed  $id
     * @param  array  $attributes
     * @param  bool   $touch
     * @return int
     */
    
public function updateExistingPivot($id, array $attributes, $touch = true)
    {
        if (
in_array($this->updatedAt(), $this->pivotColumns)) {
            
$attributes = $this->setTimestampsOnAttach($attributes, true);
        }

        
$updated = $this->newPivotStatementForId($id)->update($attributes);

        if (
$touch) {
            
$this->touchIfTouching();
        }

        return 
$updated;
    }

    
/**
     * Attach a model to the parent.
     *
     * @param  mixed  $id
     * @param  array  $attributes
     * @param  bool   $touch
     * @return void
     */
    
public function attach($id, array $attributes = [], $touch = true)
    {
        if (
$id instanceof Model) {
            
$id = $id->getKey();
        }

        if (
$id instanceof Collection) {
            
$id = $id->modelKeys();
        }

        
$query = $this->newPivotStatement();

        
$query->insert($this->createAttachRecords((array) $id, $attributes));

        if (
$touch) {
            
$this->touchIfTouching();
        }
    }

    
/**
     * Create an array of records to insert into the pivot table.
     *
     * @param  array  $ids
     * @param  array  $attributes
     * @return array
     */
    
protected function createAttachRecords($ids, array $attributes)
    {
        
$records = [];

        
$timed = ($this->hasPivotColumn($this->createdAt()) ||
                  
$this->hasPivotColumn($this->updatedAt()));

        
// To create the attachment records, we will simply spin through the IDs given
        // and create a new record to insert for each ID. Each ID may actually be a
        // key in the array, with extra attributes to be placed in other columns.
        
foreach ($ids as $key => $value) {
            
$records[] = $this->attacher($key, $value, $attributes, $timed);
        }

        return 
$records;
    }

    
/**
     * Create a full attachment record payload.
     *
     * @param  int    $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @param  bool   $timed
     * @return array
     */
    
protected function attacher($key, $value, $attributes, $timed)
    {
        list(
$id, $extra) = $this->getAttachId($key, $value, $attributes);

        
// To create the attachment records, we will simply spin through the IDs given
        // and create a new record to insert for each ID. Each ID may actually be a
        // key in the array, with extra attributes to be placed in other columns.
        
$record = $this->createAttachRecord($id, $timed);

        return 
array_merge($record, $extra);
    }

    
/**
     * Get the attach record ID and extra attributes.
     *
     * @param  mixed  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return array
     */
    
protected function getAttachId($key, $value, array $attributes)
    {
        if (
is_array($value)) {
            return [
$key, array_merge($value, $attributes)];
        }

        return [
$value, $attributes];
    }

    
/**
     * Create a new pivot attachment record.
     *
     * @param  int   $id
     * @param  bool  $timed
     * @return array
     */
    
protected function createAttachRecord($id, $timed)
    {
        
$record[$this->foreignKey] = $this->parent->getKey();

        
$record[$this->otherKey] = $id;

        
// If the record needs to have creation and update timestamps, we will make
        // them by calling the parent model's "freshTimestamp" method which will
        // provide us with a fresh timestamp in this model's preferred format.
        
if ($timed) {
            
$record = $this->setTimestampsOnAttach($record);
        }

        return 
$record;
    }

    
/**
     * Set the creation and update timestamps on an attach record.
     *
     * @param  array  $record
     * @param  bool   $exists
     * @return array
     */
    
protected function setTimestampsOnAttach(array $record, $exists = false)
    {
        
$fresh = $this->parent->freshTimestamp();

        if (! 
$exists && $this->hasPivotColumn($this->createdAt())) {
            
$record[$this->createdAt()] = $fresh;
        }

        if (
$this->hasPivotColumn($this->updatedAt())) {
            
$record[$this->updatedAt()] = $fresh;
        }

        return 
$record;
    }

    
/**
     * Detach models from the relationship.
     *
     * @param  int|array  $ids
     * @param  bool  $touch
     * @return int
     */
    
public function detach($ids = [], $touch = true)
    {
        if (
$ids instanceof Model) {
            
$ids = (array) $ids->getKey();
        }

        
$query = $this->newPivotQuery();

        
// If associated IDs were passed to the method we will only delete those
        // associations, otherwise all of the association ties will be broken.
        // We'll return the numbers of affected rows when we do the deletes.
        
$ids = (array) $ids;

        if (
count($ids) > 0) {
            
$query->whereIn($this->otherKey, (array) $ids);
        }

        
// Once we have all of the conditions set on the statement, we are ready
        // to run the delete on the pivot table. Then, if the touch parameter
        // is true, we will go ahead and touch all related models to sync.
        
$results = $query->delete();

        if (
$touch) {
            
$this->touchIfTouching();
        }

        return 
$results;
    }

    
/**
     * If we're touching the parent model, touch.
     *
     * @return void
     */
    
public function touchIfTouching()
    {
        if (
$this->touchingParent()) {
            
$this->getParent()->touch();
        }

        if (
$this->getParent()->touches($this->relationName)) {
            
$this->touch();
        }
    }

    
/**
     * Determine if we should touch the parent on sync.
     *
     * @return bool
     */
    
protected function touchingParent()
    {
        return 
$this->getRelated()->touches($this->guessInverseRelation());
    }

    
/**
     * Attempt to guess the name of the inverse of the relation.
     *
     * @return string
     */
    
protected function guessInverseRelation()
    {
        return 
Str::camel(Str::plural(class_basename($this->getParent())));
    }

    
/**
     * Create a new query builder for the pivot table.
     *
     * @return IlluminateDatabaseQueryBuilder
     */
    
protected function newPivotQuery()
    {
        
$query = $this->newPivotStatement();

        foreach (
$this->pivotWheres as $whereArgs) {
            
call_user_func_array([$query, 'where'], $whereArgs);
        }

        return 
$query->where($this->foreignKey, $this->parent->getKey());
    }

    
/**
     * Get a new plain query builder for the pivot table.
     *
     * @return IlluminateDatabaseQueryBuilder
     */
    
public function newPivotStatement()
    {
        return 
$this->query->getQuery()->newQuery()->from($this->table);
    }

    
/**
     * Get a new pivot statement for a given "other" ID.
     *
     * @param  mixed  $id
     * @return IlluminateDatabaseQueryBuilder
     */
    
public function newPivotStatementForId($id)
    {
        return 
$this->newPivotQuery()->where($this->otherKey, $id);
    }

    
/**
     * Create a new pivot model instance.
     *
     * @param  array  $attributes
     * @param  bool   $exists
     * @return IlluminateDatabaseEloquentRelationsPivot
     */
    
public function newPivot(array $attributes = [], $exists = false)
    {
        
$pivot = $this->related->newPivot($this->parent, $attributes, $this->table, $exists);

        return 
$pivot->setPivotKeys($this->foreignKey, $this->otherKey);
    }

    
/**
     * Create a new existing pivot model instance.
     *
     * @param  array  $attributes
     * @return IlluminateDatabaseEloquentRelationsPivot
     */
    
public function newExistingPivot(array $attributes = [])
    {
        return 
$this->newPivot($attributes, true);
    }

    
/**
     * Set the columns on the pivot table to retrieve.
     *
     * @param  array|mixed  $columns
     * @return $this
     */
    
public function withPivot($columns)
    {
        
$columns = is_array($columns) ? $columns : func_get_args();

        
$this->pivotColumns = array_merge($this->pivotColumns, $columns);

        return 
$this;
    }

    
/**
     * Specify that the pivot table has creation and update timestamps.
     *
     * @param  mixed  $createdAt
     * @param  mixed  $updatedAt
     * @return IlluminateDatabaseEloquentRelationsBelongsToMany
     */
    
public function withTimestamps($createdAt = null, $updatedAt = null)
    {
        
$this->pivotCreatedAt = $createdAt;
        
$this->pivotUpdatedAt = $updatedAt;

        return 
$this->withPivot($this->createdAt(), $this->updatedAt());
    }

    
/**
     * Get the name of the "created at" column.
     *
     * @return string
     */
    
public function createdAt()
    {
        return 
$this->pivotCreatedAt ?: $this->parent->getCreatedAtColumn();
    }

    
/**
     * Get the name of the "updated at" column.
     *
     * @return string
     */
    
public function updatedAt()
    {
        return 
$this->pivotUpdatedAt ?: $this->parent->getUpdatedAtColumn();
    }

    
/**
     * Get the related model's updated at column name.
     *
     * @return string
     */
    
public function getRelatedFreshUpdate()
    {
        return [
$this->related->getUpdatedAtColumn() => $this->related->freshTimestamp()];
    }

    
/**
     * Get the key for comparing against the parent key in "has" query.
     *
     * @return string
     */
    
public function getHasCompareKey()
    {
        return 
$this->getForeignKey();
    }

    
/**
     * Get the fully qualified foreign key for the relation.
     *
     * @return string
     */
    
public function getForeignKey()
    {
        return 
$this->table.'.'.$this->foreignKey;
    }

    
/**
     * Get the fully qualified "other key" for the relation.
     *
     * @return string
     */
    
public function getOtherKey()
    {
        return 
$this->table.'.'.$this->otherKey;
    }

    
/**
     * Get the intermediate table for the relationship.
     *
     * @return string
     */
    
public function getTable()
    {
        return 
$this->table;
    }

    
/**
     * Get the relationship name for the relationship.
     *
     * @return string
     */
    
public function getRelationName()
    {
        return 
$this->relationName;
    }
}
Онлайн: 1
Реклама