2014-11-29 17 views
6

में एक-से-कई रिश्तों को सिंक्रनाइज़ करना यदि मेरे पास कई से अधिक रिश्ते हैं तो sync विधि के साथ संबंध अपडेट करना बहुत आसान है।लैरवेल

लेकिन मैं एक से कई रिश्तों को सिंक्रनाइज़ करने के लिए क्या उपयोग करूंगा?

  • तालिका posts: id, name
  • तालिका links: id, name, post_id

यहाँ, प्रत्येक Post कई Link रों हो सकता है।

मैं डेटाबेस में किसी विशिष्ट पोस्ट से जुड़े लिंक को सिंक्रनाइज़ करना चाहता हूं, लिंक के इनपुट संग्रह के विरुद्ध (उदाहरण के लिए, एक सीआरयूडी फॉर्म से जहां मैं लिंक जोड़ सकता हूं, निकाल सकता हूं और संशोधित कर सकता हूं)।

डेटाबेस में लिंक जो मेरे इनपुट संग्रह में मौजूद नहीं हैं हटा दिया जाना चाहिए। डेटाबेस में मौजूद होने वाले लिंक और मेरे इनपुट में इनपुट को प्रतिबिंबित करने के लिए अद्यतन किया जाना चाहिए, और केवल मेरे इनपुट में मौजूद लिंक डेटाबेस में नए रिकॉर्ड के रूप में जोड़े जाने चाहिए।

वांछित व्यवहार को संक्षेप में प्रस्तुत करने के लिए:

  • inputArray = सच/db = false --- बनाएं
  • inputArray = false/db = सच --- DELETE
  • inputArray = सच/db = सच ---- अद्यतन

उत्तर

6

दुर्भाग्य से एक से कई संबंधों के लिए sync विधि नहीं है। अपने आप इसे करने के लिए बहुत आसान है। कम से कम अगर आपके पास कोई विदेशी कुंजी links संदर्भित नहीं है। क्योंकि तब आप पंक्तियों को सरल बना सकते हैं और उन्हें फिर से सम्मिलित कर सकते हैं।

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

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

तुम सच में एक (जो भी कारण के लिए) मौजूदा अद्यतन करने के लिए आप वास्तव में क्या आप अपने प्रश्न में वर्णित करने की जरूरत की जरूरत है।

+0

आह ठीक है, इसलिए मैं आपके साथ जाने का प्रयास करता हूं – user2834172

0

संबंधित इकाइयों को हटाने और पढ़ने में समस्या यह है कि यह उन बच्चों की संस्थाओं पर आपके पास होने वाली किसी भी विदेशी कुंजी बाधाओं को तोड़ देगा।

एक बेहतर समाधान एक sync विधि शामिल करने के लिए Laravel के HasMany संबंध संशोधित करने के लिए है:

<?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; 
    } 
} 

आप मानक HasMany संबंध के बजाय HasManySyncable उपयोग करने के लिए सुवक्ता के Model वर्ग ओवरराइड कर सकते हैं:

<?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, आप की तरह कुछ कर सकते हैं: इस बहुआयामी सरणी कि एक id कि बच्चे इकाई तालिका (links) मैचों में

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

कोई रिकॉर्ड अद्यतन किया जाएगा।तालिका में रिकॉर्ड्स जो इस सरणी में मौजूद नहीं हैं हटा दिए जाएंगे। उस सरणी में रिकॉर्ड्स जो तालिका में मौजूद नहीं हैं (एक गैर-मिलान id, या id शून्य के) को "नए" रिकॉर्ड माना जाएगा और डेटाबेस में डाला जाएगा।

+0

क्या यह लार्वा के डिफ़ॉल्ट को प्रभावित कर सकता है-आगे के संचालन में कई रिश्ते हैं? –

0

मैं इस तरह किया है, और यह न्यूनतम क्वेरी और कम से कम अद्यतन के लिए अनुकूलित है: $linkIds और अपने स्वयं के चर में पोस्ट मॉडल: $post

पहले, एक सरणी में सिंक करने के लिए लिंक आईडी डाल

Link::where('post_id','=',$post->id)->whereNotIn('id',$linkIds)//only remove unmatching 
    ->update(['post_id'=>null]); 
if($linkIds){//If links are empty the second query is useless 
    Link::whereRaw('(post_id is null OR post_id<>'.$post->id.')')//Don't update already matching, I am using Raw to avoid a nested or, you can use nested OR 
     ->whereIn('id',$linkIds)->update(['post_id'=>$post->id]); 
}