2014-11-29 3 views
6

Если у меня есть отношения «многие ко многим», очень легко обновить отношения с помощью метода sync.Синхронизация отношений «один ко многим» в Laravel

Но что бы я использовал, чтобы синхронизировать отношения «один ко многим»?

  • стол posts: id, name
  • стол links: id, name, post_id

Здесь каждый Post может иметь несколько Link с.

Я хотел бы синхронизировать ссылки, связанные с определенным сообщением в базе данных, против введенного набора ссылок (например, из формы CRUD, где я могу добавлять, удалять и изменять ссылки).

Ссылки в базе данных, которые отсутствуют в моей коллекции входных данных, должны быть удалены. Ссылки, которые существуют в базе данных и на моем входе, должны быть обновлены, чтобы отражать входные данные, а ссылки, которые присутствуют только на моем входе, должны быть добавлены в качестве новых записей в базе данных.

Резюмируя желаемое поведение:

  • inputArray = истина/ложь дБ = --- CREATE
  • inputArray = ложь/дб = истина --- DELETE
  • inputArray = истина/дб = true ---- UPDATE

ответ

6

К сожалению, для отношений «один ко многим» не существует метода sync. Это довольно просто сделать это самостоятельно. По крайней мере, если у вас нет внешнего ключа, ссылающегося на links. Потому что тогда вы можете просто удалить строки и вставить их снова.

$links = array(
    new Link(), 
    new Link() 
); 

$post->links()->delete(); 
$post->links()->saveMany($links); 

Если вам действительно необходимо обновить существующую (по какой-либо причине), вам необходимо выполнить именно то, что вы описали в своем вопросе.

+0

ah ok Спасибо, поэтому я пытаюсь пойти с тобой в путь – user2834172

0

Проблема с удалением и считыванием связанных объектов заключается в том, что он нарушит любые ограничения внешнего ключа, которые могут возникнуть у этих дочерних объектов.

Лучшее решение изменить HasMany отношения Laravel к включают в себя sync метод:

<?php 

namespace App\Model\Relations; 

use Illuminate\Database\Eloquent\Relations\HasMany; 

/** 
* @link https://github.com/laravel/framework/blob/5.4/src/Illuminate/Database/Eloquent/Relations/HasMany.php 
*/ 
class HasManySyncable extends HasMany 
{ 
    public function sync($data, $deleting = true) 
    { 
     $changes = [ 
      'created' => [], 'deleted' => [], 'updated' => [], 
     ]; 

     $relatedKeyName = $this->related->getKeyName(); 

     // First we need to attach any of the associated models that are not currently 
     // in the child entity 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->newQuery()->pluck(
      $relatedKeyName 
     )->all(); 

     // Separate the submitted data into "update" and "new" 
     $updateRows = []; 
     $newRows = []; 
     foreach ($data as $row) { 
      // We determine "updateable" rows as those whose $relatedKeyName (usually 'id') is set, not empty, and 
      // match a related row in the database. 
      if (isset($row[$relatedKeyName]) && !empty($row[$relatedKeyName]) && in_array($row[$relatedKeyName], $current)) { 
       $id = $row[$relatedKeyName]; 
       $updateRows[$id] = $row; 
      } else { 
       $newRows[] = $row; 
      } 
     } 

     // Next, we'll determine the rows in the database that aren't in the "update" list. 
     // These rows will be scheduled for deletion. Again, we determine based on the relatedKeyName (typically 'id'). 
     $updateIds = array_keys($updateRows); 
     $deleteIds = []; 
     foreach ($current as $currentId) { 
      if (!in_array($currentId, $updateIds)) { 
       $deleteIds[] = $currentId; 
      } 
     } 

     // Delete any non-matching rows 
     if ($deleting && count($deleteIds) > 0) { 
      $this->getRelated()->destroy($deleteIds); 

      $changes['deleted'] = $this->castKeys($deleteIds); 
     } 

     // Update the updatable rows 
     foreach ($updateRows as $id => $row) { 
      $this->getRelated()->where($relatedKeyName, $id) 
       ->update($row); 
     } 

     $changes['updated'] = $this->castKeys($updateIds); 

     // Insert the new rows 
     $newIds = []; 
     foreach ($newRows as $row) { 
      $newModel = $this->create($row); 
      $newIds[] = $newModel->$relatedKeyName; 
     } 

     $changes['created'][] = $this->castKeys($newIds); 

     return $changes; 
    } 


    /** 
    * 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) array_map(function ($v) { 
      return $this->castKey($v); 
     }, $keys); 
    } 

    /** 
    * Cast the given key to an integer if it is numeric. 
    * 
    * @param mixed $key 
    * @return mixed 
    */ 
    protected function castKey($key) 
    { 
     return is_numeric($key) ? (int) $key : (string) $key; 
    } 
} 

Вы можете переопределить Model класс красноречив, чтобы использовать HasManySyncable вместо стандартного HasMany отношений:

<?php 

namespace App\Model; 

use App\Model\Relations\HasManySyncable; 
use Illuminate\Database\Eloquent\Model; 

abstract class MyBaseModel extends Model 
{ 
    /** 
    * Overrides the default Eloquent hasMany relationship to return a HasManySyncable. 
    * 
    * {@inheritDoc} 
    * @return \App\Model\Relations\HasManySyncable 
    */ 
    public function hasMany($related, $foreignKey = null, $localKey = null) 
    { 
     $instance = $this->newRelatedInstance($related); 

     $foreignKey = $foreignKey ?: $this->getForeignKey(); 

     $localKey = $localKey ?: $this->getKeyName(); 

     return new HasManySyncable(
      $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey 
     ); 
    } 

Предположив что ваша модель Post распространяется на MyBaseModel и имеет links()hasMany r elationship, вы можете сделать что-то вроде:

$post->links()->sync([ 
    [ 
     'id' => 21, 
     'name' => "LinkedIn profile" 
    ], 
    [ 
     'id' => null, 
     'label' => "Personal website" 
    ] 
]); 

Любые записи в этом многомерном массиве, которые оказывают id, соответствующий дочерней сущности (таблица links) будет обновляться.Записи в таблице, отсутствующие в этом массиве, будут удалены. Записи в массиве, которые не присутствуют в таблице (имеют несоответствие id или id нулевой), будут считаться «новыми» записями и будут вставлены в базу данных.

+0

Может ли это повлиять на laravel по умолчанию имеет-многие отношения в дальнейших операциях? –

0

Я сделал, как это, и он оптимизирован для минимальных запросов и минимальных обновлений:

первый, положить ссылку идентификаторами для синхронизации в массиве: $linkIds и пост модели в своей собственной переменной: $post

Смежные вопросы