Вход Регистрация
Файл: system/vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php
Строк: 570
<?php

namespace IlluminateDatabaseEloquentRelationsConcerns;

use 
IlluminateDatabaseEloquentCollection;
use 
IlluminateDatabaseEloquentModel;
use 
IlluminateDatabaseEloquentRelationsPivot;
use 
IlluminateSupportCollection as BaseCollection;

trait 
InteractsWithPivotTable
{
    
/**
     * Toggles a model (or models) from the parent.
     *
     * Each existing model is detached, and non existing ones are attached.
     *
     * @param  mixed  $ids
     * @param  bool  $touch
     * @return array
     */
    
public function toggle($ids$touch true)
    {
        
$changes = [
            
'attached' => [], 'detached' => [],
        ];

        
$records $this->formatRecordsList($this->parseIds($ids));

        
// Next, we will determine which IDs should get removed from the join table by
        // checking which of the given ID/records is in the list of current records
        // and removing all of those rows from this "intermediate" joining table.
        
$detach array_values(array_intersect(
            
$this->newPivotQuery()->pluck($this->relatedPivotKey)->all(),
            
array_keys($records)
        ));

        if (
count($detach) > 0) {
            
$this->detach($detachfalse);

            
$changes['detached'] = $this->castKeys($detach);
        }

        
// Finally, for all of the records which were not "detached", we'll attach the
        // records into the intermediate table. Then, we will add those attaches to
        // this change list and get ready to return these results to the callers.
        
$attach array_diff_key($recordsarray_flip($detach));

        if (
count($attach) > 0) {
            
$this->attach($attach, [], false);

            
$changes['attached'] = array_keys($attach);
        }

        
// Once we have finished attaching or detaching the records, we will see if we
        // have done any attaching or detaching, and if we have we will touch these
        // relationships if they are configured to touch on any database updates.
        
if ($touch && (count($changes['attached']) ||
                       
count($changes['detached']))) {
            
$this->touchIfTouching();
        }

        return 
$changes;
    }

    
/**
     * Sync the intermediate tables with a list of IDs without detaching.
     *
     * @param  IlluminateSupportCollection|IlluminateDatabaseEloquentModel|array  $ids
     * @return array
     */
    
public function syncWithoutDetaching($ids)
    {
        return 
$this->sync($idsfalse);
    }

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

        
// 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->getCurrentlyAttachedPivots()
                        ->
pluck($this->relatedPivotKey)->all();

        
$detach array_diff($currentarray_keys(
            
$records $this->formatRecordsList($this->parseIds($ids))
        ));

        
// 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
        // array of the new IDs given to the method which will complete the sync.
        
if ($detaching && count($detach) > 0) {
            
$this->detach($detach);

            
$changes['detached'] = $this->castKeys($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$currentfalse)
        );

        
// Once we have finished attaching or detaching the records, we will see if we
        // have done any attaching or detaching, and if we have we will touch these
        // relationships if they are configured to touch on any database updates.
        
if (count($changes['attached']) ||
            
count($changes['updated'])) {
            
$this->touchIfTouching();
        }

        return 
$changes;
    }

    
/**
     * Format the sync / toggle record list so that it is keyed by ID.
     *
     * @param  array  $records
     * @return array
     */
    
protected function formatRecordsList(array $records)
    {
        return 
collect($records)->mapWithKeys(function ($attributes$id) {
            if (! 
is_array($attributes)) {
                [
$id$attributes] = [$attributes, []];
            }

            return [
$id => $attributes];
        })->
all();
    }

    
/**
     * Attach all of the records that aren't in the given current records.
     *
     * @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'][] = $this->castKey($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) > &&
                
$this->updateExistingPivot($id$attributes$touch)) {
                
$changes['updated'][] = $this->castKey($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 (
$this->using && empty($this->pivotWheres) && empty($this->pivotWhereIns)) {
            return 
$this->updateExistingPivotUsingCustomClass($id$attributes$touch);
        }

        if (
in_array($this->updatedAt(), $this->pivotColumns)) {
            
$attributes $this->addTimestampsToAttachment($attributestrue);
        }

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

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

        return 
$updated;
    }

    
/**
     * Update an existing pivot record on the table via a custom class.
     *
     * @param  mixed  $id
     * @param  array  $attributes
     * @param  bool  $touch
     * @return int
     */
    
protected function updateExistingPivotUsingCustomClass($id, array $attributes$touch)
    {
        
$pivot $this->getCurrentlyAttachedPivots()
                    ->
where($this->foreignPivotKey$this->parent->{$this->parentKey})
                    ->
where($this->relatedPivotKey$this->parseId($id))
                    ->
first();

        
$updated $pivot $pivot->fill($attributes)->isDirty() : false;

        if (
$updated) {
            
$pivot->save();
        }

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

        return (int) 
$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 (
$this->using) {
            
$this->attachUsingCustomClass($id$attributes);
        } else {
            
// Here we will insert the attachment records into the pivot table. Once we have
            // inserted the records, we will touch the relationships if necessary and the
            // function will return. We can parse the IDs before inserting the records.
            
$this->newPivotStatement()->insert($this->formatAttachRecords(
                
$this->parseIds($id), $attributes
            
));
        }

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

    
/**
     * Attach a model to the parent using a custom class.
     *
     * @param  mixed  $id
     * @param  array  $attributes
     * @return void
     */
    
protected function attachUsingCustomClass($id, array $attributes)
    {
        
$records $this->formatAttachRecords(
            
$this->parseIds($id), $attributes
        
);

        foreach (
$records as $record) {
            
$this->newPivot($recordfalse)->save();
        }
    }

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

        
$hasTimestamps = ($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->formatAttachRecord(
                
$key$value$attributes$hasTimestamps
            
);
        }

        return 
$records;
    }

    
/**
     * Create a full attachment record payload.
     *
     * @param  int  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @param  bool  $hasTimestamps
     * @return array
     */
    
protected function formatAttachRecord($key$value$attributes$hasTimestamps)
    {
        [
$id$attributes] = $this->extractAttachIdAndAttributes($key$value$attributes);

        return 
array_merge(
            
$this->baseAttachRecord($id$hasTimestamps), $this->castAttributes($attributes)
        );
    }

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

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

        
$record[$this->foreignPivotKey] = $this->parent->{$this->parentKey};

        
// 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->addTimestampsToAttachment($record);
        }

        foreach (
$this->pivotValues as $value) {
            
$record[$value['column']] = $value['value'];
        }

        return 
$record;
    }

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

        if (
$this->using) {
            
$pivotModel = new $this->using;

            
$fresh $fresh->format($pivotModel->getDateFormat());
        }

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

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

        return 
$record;
    }

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

    
/**
     * Detach models from the relationship.
     *
     * @param  mixed  $ids
     * @param  bool  $touch
     * @return int
     */
    
public function detach($ids null$touch true)
    {
        if (
$this->using && ! empty($ids) && empty($this->pivotWheres) && empty($this->pivotWhereIns)) {
            
$results $this->detachUsingCustomClass($ids);
        } else {
            
$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.
            
if (! is_null($ids)) {
                
$ids $this->parseIds($ids);

                if (empty(
$ids)) {
                    return 
0;
                }

                
$query->whereIn($this->relatedPivotKey, (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;
    }

    
/**
     * Detach models from the relationship using a custom class.
     *
     * @param  mixed  $ids
     * @return int
     */
    
protected function detachUsingCustomClass($ids)
    {
        
$results 0;

        foreach (
$this->parseIds($ids) as $id) {
            
$results += $this->newPivot([
                
$this->foreignPivotKey => $this->parent->{$this->parentKey},
                
$this->relatedPivotKey => $id,
            ], 
true)->delete();
        }

        return 
$results;
    }

    
/**
     * Get the pivot models that are currently attached.
     *
     * @return IlluminateSupportCollection
     */
    
protected function getCurrentlyAttachedPivots()
    {
        return 
$this->newPivotQuery()->get()->map(function ($record) {
            
$class $this->using $this->using Pivot::class;

            
$pivot $class::fromRawAttributes($this->parent, (array) $record$this->getTable(), true);

            return 
$pivot->setPivotKeys($this->foreignPivotKey$this->relatedPivotKey);
        });
    }

    
/**
     * 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$this->using
        
);

        return 
$pivot->setPivotKeys($this->foreignPivotKey$this->relatedPivotKey);
    }

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

    
/**
     * 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()->whereIn($this->relatedPivotKey$this->parseIds($id));
    }

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

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

        foreach (
$this->pivotWhereIns as $arguments) {
            
call_user_func_array([$query'whereIn'], $arguments);
        }

        return 
$query->where($this->foreignPivotKey$this->parent->{$this->parentKey});
    }

    
/**
     * Set the columns on the pivot table to retrieve.
     *
     * @param  array|mixed  $columns
     * @return $this
     */
    
public function withPivot($columns)
    {
        
$this->pivotColumns array_merge(
            
$this->pivotColumnsis_array($columns) ? $columns func_get_args()
        );

        return 
$this;
    }

    
/**
     * Get all of the IDs from the given mixed value.
     *
     * @param  mixed  $value
     * @return array
     */
    
protected function parseIds($value)
    {
        if (
$value instanceof Model) {
            return [
$value->{$this->relatedKey}];
        }

        if (
$value instanceof Collection) {
            return 
$value->pluck($this->relatedKey)->all();
        }

        if (
$value instanceof BaseCollection) {
            return 
$value->toArray();
        }

        return (array) 
$value;
    }

    
/**
     * Get the ID from the given mixed value.
     *
     * @param  mixed  $value
     * @return mixed
     */
    
protected function parseId($value)
    {
        return 
$value instanceof Model $value->{$this->relatedKey} : $value;
    }

    
/**
     * Cast the given keys to integers if they are numeric and string otherwise.
     *
     * @param  array  $keys
     * @return array
     */
    
protected function castKeys(array $keys)
    {
        return 
array_map(function ($v) {
            return 
$this->castKey($v);
        }, 
$keys);
    }

    
/**
     * Cast the given key to convert to primary key type.
     *
     * @param  mixed  $key
     * @return mixed
     */
    
protected function castKey($key)
    {
        return 
$this->getTypeSwapValue(
            
$this->related->getKeyType(),
            
$key
        
);
    }

    
/**
     * Cast the given pivot attributes.
     *
     * @param  array  $attributes
     * @return array
     */
    
protected function castAttributes($attributes)
    {
        return 
$this->using
                    
$this->newPivot()->fill($attributes)->getAttributes()
                    : 
$attributes;
    }

    
/**
     * Converts a given value to a given type value.
     *
     * @param  string  $type
     * @param  mixed  $value
     * @return mixed
     */
    
protected function getTypeSwapValue($type$value)
    {
        switch (
strtolower($type)) {
            case 
'int':
            case 
'integer':
                return (int) 
$value;
            case 
'real':
            case 
'float':
            case 
'double':
                return (float) 
$value;
            case 
'string':
                return (string) 
$value;
            default:
                return 
$value;
        }
    }
}
Онлайн: 0
Реклама