2016-09-19 78 views
0

我目前正在使用Model事件在數據庫中執行數據驗證/同步的Laravel應用程序。如何知道laravel中給定模型上的關係是否已經更新?

我有一個包含重要數據的表。此表在不同型號的更新中更新。我使用模型事件來處理它。它適用於模型屬性示例:

<?php 
Product::saved(function (Product $p) { 
    $dirty = collect($p->getDirty()); 
    if ($dirty->has('ugr') || $dirty->has('ra')) { 
     //Do some stuff here 
    } 
}); 

使用這種邏輯,我可以限制我在特定模型屬性更新上執行「stuff」操作。

如何在Product關係上進行這種檢查?

我有一個ManyToMany關係,可通過applications方法訪問Product,我怎麼能知道鏈接的應用程序列表是否自模型加載後已更改?

感謝您的幫助!

回答

2

我還沒有找到一種方法來直接與Laravel做到這一點。我已經使用應用程序事件和關係繼承構建了一個解決方案。

我添加了一個名爲trait其中App\Database\Eloquent\FollowUpdatedRelations有目標,以通知相關更新:

<?php 

namespace App\Database\Eloquent; 

use Illuminate\Database\Eloquent\Relations\BelongsToMany; 
use App\Library\Decorator; 
use App\Events\RelationUpdated; 

trait FollowUpdatedRelations 
{ 
    /** 
    * The default error bag. 
    * 
    * @var string 
    */ 
    protected $updatedRelations = []; 

    /** 
    * Check if the belongs to many relation has been updated 
    * @param BelongsToMany $relation 
    * @param array   $syncResult Result of the `sync` method call 
    * @return boolean 
    */ 
    protected function hasBeenUpdated(BelongsToMany $relation, array $syncResult) 
    { 
     if (isset($syncResult['attached']) && count($syncResult['attached']) > 0) { 
      $this->updatedRelations[$relation->getRelationName()] = true; 
      event(new RelationUpdated($relation)); 
     } elseif (isset($syncResult['detached']) && count($syncResult['detached']) > 0) { 
      $this->updatedRelations[$relation->getRelationName()] = true; 
      event(new RelationUpdated($relation)); 
     } 
    } 

    /** 
    * Decorate a BelongsToMany to listen to relation update 
    * @param BelongsToMany $relation 
    * @return Decorator 
    */ 
    protected function decorateBelongsToMany(BelongsToMany $relation) 
    { 
     $decorator = new Decorator($relation); 
     $decorator->decorate('sync', function ($decorated, $arguments) { 
      $updates = call_user_func_array([$decorated, 'sync'], $arguments); 
      $this->hasBeenUpdated($decorated, $updates); 
      return $updates; 
     }); 

     return $decorator; 
    } 

    /** 
    * Retrieve the list of dirty relations 
    * @return array 
    */ 
    public function getDirtyRelations() 
    { 
     return $this->updatedRelations; 
    } 
} 

我用這種特質在我需要遵循相關更新模型和我已經更新的關係定義:

<?php 

... 

class Product extends Model 
{ 
    use FollowUpdatedRelations; 

    .... 


    /** 
    * Defines relationship with App\Applications model 
    * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 
    */ 
    public function applications() 
    { 
     return $this->decorateBelongsToMany(
      $this->belongsToMany('App\Application', 'product_application') 
     ); 
    } 
} 

App\Library\Decorator類包裝一個對象,並添加重寫方法的能力:

<?php 

namespace App\Library; 

use Closure; 

class Decorator 
{ 
    /** 
    * Decorated instance 
    * @var mixed 
    */ 
    private $decorated; 

    private $methods = []; 

    /** 
    * Decorate given instance 
    * @param mixed $toDecorate 
    */ 
    public function __construct($toDecorate) 
    { 
     $this->decorated = $toDecorate; 
    } 

    /** 
    * Decorate a method 
    * @param string $name 
    * @param Closure $callback Method to run instead of decorated one 
    */ 
    public function decorate($name, Closure $callback) 
    { 
     $this->methods[$name] = $callback; 
     return $this; 
    } 

    /** 
    * Call a method on decorated instance 
    * @param string $name 
    * @param array $arguments 
    * @return mixed 
    */ 
    public function __call($name, $arguments) 
    { 
     if (isset($this->methods[$name])) { 
      return call_user_func_array($this->methods[$name], [$this->decorated, $arguments]); 
     } 

     return call_user_func_array([$this->decorated, $name], $arguments); 
    } 
} 

有了這個對象,我可以在BelongsToMany Laravel關係上創建我自定義的sync方法。我使用sync方法來跟蹤更新,因爲它會返回數據透視表中已連接,已分離和已更新模型的列表。

我只需要計算是否有連接或分離的模型,並分派相應的事件。我的活動是App\Events\RelationUpdated幷包含更新後的關係屬性。

然後,我可以在EventServiceProvider像添加事件偵聽器:

<?php 

namespace App\Providers; 

use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; 
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; 
use App\Events\RelationUpdated; 
use App\Product; 

class EventServiceProvider extends ServiceProvider 
{ 
    /** 
    * Register any other events for your application. 
    * 
    * @param \Illuminate\Contracts\Events\Dispatcher $events 
    * @return void 
    */ 
    public function boot(DispatcherContract $events) 
    { 
     parent::boot($events); 

     ... 

     //When a listened relation is updated, we perform a Model save 
     $events->listen(RelationUpdated::class, function ($event) { 
      //Here I do my stuff 
     }); 
    } 
} 

我可以把所有時的關係被更新時必須執行的東西。看起來有點複雜,但我認爲依靠類似的東西比在每個模型構建中添加邏輯要輕。

希望得到這個幫助!

1

沒有現成的方法來做到這一點。實際上,無論何時更新子對象,都要通知父對象。換句話說,如果任何相關的應用程序更新,您希望觸發產品上保存的處理程序。

如果我的理解是正確的,那麼您需要通過產品模型註冊處理程序,因爲如果您只想通知其相關的子模型更新的相關模型,那麼您可以簡單地在子模型中使用protected $touches屬性因此,如果任何子模型發生更改,父模型將始終更新。

因爲您只想跟蹤更改,所以您需要通過父/主模型本身在相關模型上註冊已保存的處理程序(加載時急切地)加載相關模型。如果我仍然在正確的軌道上,那麼,你可以嘗試這樣的事情:

$product = Product::with('applications')->findOrFail($id); 

// Check if there are any Application loaded (eagerly) with Product 
if($product->relationLoaded('applications')) { 

    // Loop through each Application instance loaded with Product 
    $product->applications->each(function($application) use($product) { 

     // Register the saved event handler on each Application instance 
     $application->saved(function($app) use($product) { 

      // If nothing has been changed do nothing 
      if(!count($dirty = $app->getDirty())) return true; 

      // Check if prop_1 or prop_2 has been changed in $app 
      $props = ['prop_1', 'prop_2']; // You may add more props 

      $keys = array_intersect_key($props, array_keys($dirty)); 

      if(count($keys)) { 
       // Do some stuff here 
       // You can do something with $product as well 
       // If you have saved handler on Product then just try 
       // $product->touch(); // it'll trigger that handler on Product 
      } 
     }); 
    }); 
} 

希望,你想要像我上面描述的東西。順便說一句,這只是一個想法,你可以使用單獨的類/方法在子模型上註冊事件處理程序,以保持代碼清潔。

+0

您的解決方案似乎有效,但對我來說有點複雜。註冊每個加載的應用程序,與產品相關的,已保存的處理程序...此外,我使用數據透視表,因此相關模型不是真正的模型,它是一個透視圖... 昨天晚上我使用了一個解決方案應用程序事件和事件監聽器。我今天將提交它作爲答案。 – shulard

+0

當然,請提交您的解決方案,這對未來的訪問者和我也有幫助,以便了解它。 –

+0

我已經添加了我的解決方案,您可以查看一下嗎? – shulard

相關問題