Skip to content

Instantly share code, notes, and snippets.

@danrichards
Last active June 5, 2020 02:42
  • Select an option

Select an option

Revisions

  1. danrichards revised this gist Nov 17, 2017. 1 changed file with 0 additions and 3 deletions.
    3 changes: 0 additions & 3 deletions Model.php
    Original file line number Diff line number Diff line change
    @@ -2,9 +2,6 @@

    namespace App;

    use App\Traits\LogsExceptions;
    use App\Traits\Models\ActionTracking;
    use App\Traits\Models\NotifiableSupplement;
    use App\Utils\Data;
    use App\Utils\Str;
    use Cache;
  2. danrichards revised this gist Nov 17, 2017. 1 changed file with 152 additions and 4 deletions.
    156 changes: 152 additions & 4 deletions Model.php
    Original file line number Diff line number Diff line change
    @@ -2,20 +2,51 @@

    namespace App;

    use App\Traits\LogsExceptions;
    use App\Traits\Models\ActionTracking;
    use App\Traits\Models\NotifiableSupplement;
    use App\Utils\Data;
    use App\Utils\Str;
    use Cache;
    use Closure;
    use DateTime;
    use DB;
    use Illuminate\Database\Eloquent\Model as BaseModel;
    use Illuminate\Notifications\Notifiable;

    /**
    * Class Model
    *
    * Please discuss any changes to this Class with Dan before making them.
    *
    * @method static \Illuminate\Database\Eloquent\Builder whereMorphedBy(\Illuminate\Database\Eloquent\Model|string $model, string $morph = null)
    * @method static \Illuminate\Database\Eloquent\Builder whereMorph(\Illuminate\Database\Eloquent\Model $model, string $morph = null)
    * @method static \Illuminate\Database\Eloquent\Builder whereMorphNull(string $morph = null)
    * @method static \Illuminate\Database\Eloquent\Builder whereMorphNotNull(string $morph = null)
    * @method static \Illuminate\Database\Eloquent\Builder joinExists(string $table)
    */
    abstract class Model extends BaseModel
    {

    use LogsExceptions,
    ActionTracking,
    NotifiableSupplement,
    Notifiable {
    notify as parent_notify;
    }

    /**
    * Laravel's getMutatedAttributes() method will not fetch mutators that are
    * within a trait. We need to preserve mutators (for fields) saved to the
    * attributes property.
    *
    * @see \App\Utils\Data::withMutatedAttributes()
    * @var array $trait_mutators
    */
    protected static $trait_mutators_with_fields = [
    'value'
    ];

    /**
    * If you want to specify the result of a Builder::get() as a property, you
    * may do so by specifying a query property. All this does is magically
    @@ -132,6 +163,7 @@ private function cacheRelated($attribute, $minutes, Closure $callback)

    /**
    * @param string|array|null $attribute
    * @param null $id
    */
    public function cacheAttributeBust($attribute = null, $id = null)
    {
    @@ -232,6 +264,66 @@ public function __get($name)
    return parent::__get($name);
    }

    /**
    * Set an individual model attribute. No checking is done.
    *
    * @param string $key
    * @param mixed $value
    * @param bool $sync
    * @return $this
    */
    public function setRawAttribute($key, $value, $sync = false)
    {
    $this->attributes[$key] = $value;

    if ($sync) {
    $this->syncOriginal();
    }

    return $this;
    }

    /**
    * @param $query
    * @param Model $model
    * @param null $related_field
    * @return \Illuminate\Database\Eloquent\Builder
    */
    public function scopeWhereModel($query, Model $model, $related_field = null)
    {
    $model_class = get_class($model);
    $model_id = $model->getKey();

    if (empty($related_field)) {
    $exploded = explode('\\', $model_class);
    $related_field = array_pop($exploded);
    $related_field = strtolower(Str::snake(Str::singular($related_field)));
    }

    return $query->where("{$related_field}_id", $model_id);
    }

    /**
    * @param $query
    * @param Model|string $model_or_class
    * @param null $morph
    * @return \Illuminate\Database\Eloquent\Builder
    */
    public function scopeWhereMorphedBy($query, $model_or_class, $morph = null)
    {
    $model_class = is_object($model_or_class)
    ? get_class($model_or_class)
    : $model_or_class;

    if (empty($morph)) {
    $exploded = explode('\\', $model_class);
    $morph = array_pop($exploded);
    $morph = strtolower(Str::snake(Str::singular($morph)));
    }

    return $query->where("{$morph}_type", Str::rawClass($model_class));
    }

    /**
    * @param $query
    * @param Model $model
    @@ -243,9 +335,10 @@ public function scopeWhereMorph($query, Model $model, $morph = null)
    $model_class = get_class($model);
    $model_id = $model->getKey();

    if (! empty($morph)) {
    $morph = array_pop(explode('\\', $model_class));
    $morph = strtolower(Str::singular($morph));
    if (empty($morph)) {
    $exploded = explode('\\', $model_class);
    $morph = array_pop($exploded);
    $morph = strtolower(Str::snake(Str::singular($morph)));
    }

    return $query->where("{$morph}_type", Str::rawClass($model_class))
    @@ -274,4 +367,59 @@ public function scopeWhereMorphNotNull($query, $morph)
    ->whereNotNull("{$morph}_id");
    }

    }
    /**
    * @param null $morph
    * @return array
    */
    public function compact($morph = null)
    {
    if (is_null($morph)) {
    $model_class = get_class($this);
    $exploded = explode('\\', $model_class);
    $morph = array_pop($exploded);
    $morph = strtolower(Str::snake(Str::singular($morph)));
    }

    return [
    "{$morph}_type" => get_class($this),
    "{$morph}_id" => $this->getKey()
    ];
    }

    /**
    * @param $instance
    * @return void
    */
    public function notify($instance)
    {
    if ( config(sprintf("notifications.%s.enabled", get_class($instance))) ) {
    $this->parent_notify($instance);
    }
    }

    /**
    * @param \Illuminate\Database\Eloquent\Builder $query
    * @param static|\Illuminate\Database\Eloquent\Model|string $table
    * @return bool
    */
    public function scopeJoinExists($query, $table)
    {
    if (is_object($table)) {
    $table = get_class($table);
    } elseif (class_exists($table)) {
    $model = new $table;
    /** @var static $table */
    $table = $model->getTable();
    }

    $joins = $query->getQuery()->joins ?: [];

    foreach ($joins as $j) {
    if ($j->table == $table) {
    return true;
    }
    }

    return false;
    }
    }
  3. danrichards revised this gist Nov 28, 2016. No changes.
  4. danrichards revised this gist Nov 28, 2016. 1 changed file with 75 additions and 50 deletions.
    125 changes: 75 additions & 50 deletions Model.php
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,8 @@

    namespace App;

    use App\Utils\Data;
    use App\Utils\Str;
    use Cache;
    use Closure;
    use Illuminate\Database\Eloquent\Model as BaseModel;
    @@ -46,9 +48,15 @@ abstract class Model extends BaseModel
    *
    * \Illuminate\Database\Query\Builder
    * \Illuminate\Database\Query\HasMany
    * \Illuminate\Database\Query\HasManyOne
    * \Illuminate\Database\Query\HasManyThrough
    *
    * IMPORTANT: YOU WILL RECEIVE A COLLECTION BACK
    *
    * Use $cache_attributes for:
    *
    * \Illuminate\Database\Query\BelongsTo
    * \Illuminate\Database\Query\HasOne
    *
    * @var array
    */
    protected $cache_related = [
    @@ -75,27 +83,27 @@ abstract class Model extends BaseModel
    /**
    * Simple attribute caching, for more robust caching, use a Repository
    *
    * @param string $related
    * @param string $attribute
    * @param null $id
    * @return string
    */
    private function cacheAttributeKey($related, $id = null)
    private function cacheAttributeKey($attribute, $id = null)
    {
    $id = $id ?: $this->getKey();
    return implode('|', [get_class($this), $id, 'attribute', $related]);
    return implode('|', [get_class($this), $id, 'attribute', $attribute]);
    }

    /**
    * Simple method caching, for more robust caching, use a Repository
    *
    * @param string $attribute
    * @param string $related
    * @param null $id
    * @return string
    */
    private function cacheRelatedKey($attribute, $id = null)
    private function cacheRelatedKey($related, $id = null)
    {
    $id = $id ?: $this->getKey();
    return implode('|', [get_class($this), $id, 'related', $attribute]);
    return implode('|', [get_class($this), $id, 'related', $related]);
    }

    /**
    @@ -106,7 +114,8 @@ private function cacheRelatedKey($attribute, $id = null)
    */
    private function cacheAttribute($attribute, $minutes, Closure $callback)
    {
    return Cache::remember($this->cacheAttributeKey($attribute), $minutes, $callback);
    $dynamic_cache_time = $this->dynamicCacheTime($attribute);
    return Cache::remember($this->cacheAttributeKey($attribute), $dynamic_cache_time ?: $minutes, $callback);
    }

    /**
    @@ -117,24 +126,25 @@ private function cacheAttribute($attribute, $minutes, Closure $callback)
    */
    private function cacheRelated($attribute, $minutes, Closure $callback)
    {
    return Cache::remember($this->cacheRelatedKey($attribute), $minutes, $callback);
    $dynamic_cache_time = $this->dynamicCacheTime($attribute);
    return Cache::remember($this->cacheRelatedKey($attribute), $dynamic_cache_time ?: $minutes, $callback);
    }

    /**
    * @param string|array|null $attribute
    */
    public function cacheAttributeBust($attribute = null, $parent_id)
    public function cacheAttributeBust($attribute = null, $id = null)
    {
    $attribute = is_null($attribute)
    ? static::normalizeAssociative($this->cache_attributes, 0)
    ? Data::normalizeAssociative($this->cache_attributes, 0)
    : $attribute;

    $attribute = is_string($attribute)
    ? (array) $attribute
    : $attribute;

    foreach ($attribute as $a) {
    Cache::forget($this->cacheAttributeKey($a));
    Cache::forget($this->cacheAttributeKey($a, $id));
    }
    }

    @@ -144,7 +154,7 @@ public function cacheAttributeBust($attribute = null, $parent_id)
    public function cacheRelatedBust($related = null)
    {
    $related = is_null($related)
    ? static::normalizeAssociative($this->cache_related, 0)
    ? Data::normalizeAssociative($this->cache_related, 0)
    : $related;

    $related = is_string($related)
    @@ -167,15 +177,28 @@ public static function cacheBust($parent_class, $parent_id, $sub_key, $key)
    Cache::forget(implode('|', func_get_args()));
    }

    /**
    * In case we want to store a cache time in a config file. For the purposes
    * of modifying cache times with our ENV file (not having to do a push)
    *
    * @param $attribute_or_related
    * @return integer
    */
    public function dynamicCacheTime($attribute_or_related)
    {
    $class = get_class($this);
    return config("cache.dynamic_model_cache.{$class}.{$attribute_or_related}");
    }

    /**
    * @param string $name
    * @return mixed
    */
    public function __get($name)
    {
    $query_props_normalized = static::normalizeAssociative($this->query_props, 'no-cache');
    $cache_attributes_normalized = static::normalizeAssociative($this->cache_attributes, 0);
    $cache_related_normalized = static::normalizeAssociative($this->cache_related, 0);
    $query_props_normalized = Data::normalizeAssociative($this->query_props, 'no-cache');
    $cache_attributes_normalized = Data::normalizeAssociative($this->cache_attributes, 0);
    $cache_related_normalized = Data::normalizeAssociative($this->cache_related, 0);

    /**
    * Handle query props.
    @@ -210,43 +233,45 @@ public function __get($name)
    }

    /**
    * Build out an associative array with mixed values.
    *
    * e.g.
    *
    * [
    * 'cats',
    * 'dogs' => 1
    * ]
    *
    * becomes
    *
    * [
    * 'cats' => 0,
    * 'dogs' => 1
    * ]
    *
    * @todo Move this method somewhere in a Util class.
    *
    * @param array $arr
    * @param int $default_value
    * @return array
    * @param $query
    * @param Model $model
    * @param null $morph
    * @return \Illuminate\Database\Eloquent\Builder
    */
    public static function normalizeAssociative(array $arr, $default_value = 0)
    public function scopeWhereMorph($query, Model $model, $morph = null)
    {
    $normalized = [];

    foreach($arr as $key => $value) {
    $new_key = $key;
    $new_value = $value;
    if (is_int($key)) {
    $new_key = $value;
    $new_value = $default_value;
    }
    $normalized[$new_key] = $new_value;
    $model_class = get_class($model);
    $model_id = $model->getKey();

    if (! empty($morph)) {
    $morph = array_pop(explode('\\', $model_class));
    $morph = strtolower(Str::singular($morph));
    }

    return $normalized;
    return $query->where("{$morph}_type", Str::rawClass($model_class))
    ->where("{$morph}_id", $model_id);
    }

    /**
    * @param $query
    * @param null $morph
    * @return \Illuminate\Database\Eloquent\Builder
    */
    public function scopeWhereMorphNull($query, $morph)
    {
    return $query->whereNull("{$morph}_type")
    ->whereNull("{$morph}_id");
    }

    /**
    * @param $query
    * @param null $morph
    * @return \Illuminate\Database\Eloquent\Builder
    */
    public function scopeWhereMorphNotNull($query, $morph)
    {
    return $query->whereNotNull("{$morph}_type")
    ->whereNotNull("{$morph}_id");
    }

    }
    }
  5. danrichards revised this gist Sep 21, 2016. 2 changed files with 3 additions and 3 deletions.
    4 changes: 2 additions & 2 deletions Model.php
    Original file line number Diff line number Diff line change
    @@ -126,7 +126,7 @@ private function cacheRelated($attribute, $minutes, Closure $callback)
    public function cacheAttributeBust($attribute = null, $parent_id)
    {
    $attribute = is_null($attribute)
    ? Data::normalizeAssociative($this->cache_attributes, 0)
    ? static::normalizeAssociative($this->cache_attributes, 0)
    : $attribute;

    $attribute = is_string($attribute)
    @@ -144,7 +144,7 @@ public function cacheAttributeBust($attribute = null, $parent_id)
    public function cacheRelatedBust($related = null)
    {
    $related = is_null($related)
    ? Data::normalizeAssociative($this->cache_related, 0)
    ? static::normalizeAssociative($this->cache_related, 0)
    : $related;

    $related = is_string($related)
    2 changes: 1 addition & 1 deletion ModelCacheServiceProvider.php
    Original file line number Diff line number Diff line change
    @@ -4,8 +4,8 @@

    use App\Model;
    // use App\User;
    // use App\Models\Order;
    // use App\Models\Listing;
    // use App\Models\Commission;
    use Illuminate\Support\ServiceProvider;

    /**
  6. danrichards revised this gist Aug 31, 2016. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions ModelCacheServiceProvider.php
    Original file line number Diff line number Diff line change
    @@ -2,10 +2,10 @@

    namespace App\Providers;

    use App\User;
    use App\Model;
    use App\Models\Listing;
    use App\Models\Legacy\Orders;
    // use App\User;
    // use App\Models\Order;
    // use App\Models\Listing;
    use Illuminate\Support\ServiceProvider;

    /**
  7. danrichards revised this gist Aug 31, 2016. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions Model.php
    Original file line number Diff line number Diff line change
    @@ -2,9 +2,8 @@

    namespace App;

    use Cache, Log;
    use Cache;
    use Closure;
    use App\Utils\Data;
    use Illuminate\Database\Eloquent\Model as BaseModel;

    /**
  8. danrichards revised this gist Aug 31, 2016. No changes.
  9. danrichards created this gist Aug 31, 2016.
    253 changes: 253 additions & 0 deletions Model.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,253 @@
    <?php

    namespace App;

    use Cache, Log;
    use Closure;
    use App\Utils\Data;
    use Illuminate\Database\Eloquent\Model as BaseModel;

    /**
    * Class Model
    *
    * Please discuss any changes to this Class with Dan before making them.
    */
    abstract class Model extends BaseModel
    {

    /**
    * If you want to specify the result of a Builder::get() as a property, you
    * may do so by specifying a query property. All this does is magically
    * call the a method or query scope on your call and hit it with get().
    *
    * @see \App\User::customers()
    *
    * e.g. User::find(1)->customers; // \Illuminate\Database\Eloquent\Collection
    *
    * Works with objects you can call get() on.
    *
    * e.g. instanceof:
    *
    * \Illuminate\Database\Query\Builder
    *
    * To add caching automatically, use the $cache_related property instead.
    *
    * @var array $query_props */
    protected $query_props = [
    // 'method_name', // Will not cache
    // 'ex_2' => '' // Non-integer will not cache
    // 'ex_3' => 30, // Cache for 30 minutes
    // 'ex_4' => 0 // Cache forever
    ];

    /**
    * Same as query props but more explicit in that there is caching.
    *
    * e.g. instanceof:
    *
    * \Illuminate\Database\Query\Builder
    * \Illuminate\Database\Query\HasMany
    * \Illuminate\Database\Query\HasManyOne
    * \Illuminate\Database\Query\HasManyThrough
    *
    * @var array
    */
    protected $cache_related = [
    // 'attr_name' // Cache forever
    // 'ex_2' => 0 // Cache forever
    // 'ex_3' => 30 // Cache for 30 minutes
    ];

    /**
    * Use Laravel's get attribute mutator to conveniently store cached data.
    *
    * Leverage for simple solutions for Models that do not have Repositories
    * already, if you feel like you're doing something dirty, then that means
    * you should create a new Repository for your model.
    *
    * @see https://laravel.com/docs/5.1/eloquent-mutators#accessors-and-mutators
    * @var array $cache_attributes */
    protected $cache_attributes = [
    // 'attr_name' // Cache forever
    // 'ex_2' => 0 // Cache forever
    // 'ex_3' => 30 // Cache for 30 minutes
    ];

    /**
    * Simple attribute caching, for more robust caching, use a Repository
    *
    * @param string $related
    * @param null $id
    * @return string
    */
    private function cacheAttributeKey($related, $id = null)
    {
    $id = $id ?: $this->getKey();
    return implode('|', [get_class($this), $id, 'attribute', $related]);
    }

    /**
    * Simple method caching, for more robust caching, use a Repository
    *
    * @param string $attribute
    * @param null $id
    * @return string
    */
    private function cacheRelatedKey($attribute, $id = null)
    {
    $id = $id ?: $this->getKey();
    return implode('|', [get_class($this), $id, 'related', $attribute]);
    }

    /**
    * @param string $attribute
    * @param int $minutes
    * @param Closure $callback
    * @return mixed
    */
    private function cacheAttribute($attribute, $minutes, Closure $callback)
    {
    return Cache::remember($this->cacheAttributeKey($attribute), $minutes, $callback);
    }

    /**
    * @param string $attribute
    * @param int $minutes
    * @param Closure $callback
    * @return mixed
    */
    private function cacheRelated($attribute, $minutes, Closure $callback)
    {
    return Cache::remember($this->cacheRelatedKey($attribute), $minutes, $callback);
    }

    /**
    * @param string|array|null $attribute
    */
    public function cacheAttributeBust($attribute = null, $parent_id)
    {
    $attribute = is_null($attribute)
    ? Data::normalizeAssociative($this->cache_attributes, 0)
    : $attribute;

    $attribute = is_string($attribute)
    ? (array) $attribute
    : $attribute;

    foreach ($attribute as $a) {
    Cache::forget($this->cacheAttributeKey($a));
    }
    }

    /**
    * @param string|array|null $related
    */
    public function cacheRelatedBust($related = null)
    {
    $related = is_null($related)
    ? Data::normalizeAssociative($this->cache_related, 0)
    : $related;

    $related = is_string($related)
    ? (array) $related
    : $related;

    foreach ($related as $r) {
    Cache::forget($this->cacheRelatedKey($r));
    }
    }

    /**
    * @param string $parent_class The Model
    * @param string|int $parent_id The Model's primary key value
    * @param string $sub_key A classification / category
    * @param string $key Cache key name
    */
    public static function cacheBust($parent_class, $parent_id, $sub_key, $key)
    {
    Cache::forget(implode('|', func_get_args()));
    }

    /**
    * @param string $name
    * @return mixed
    */
    public function __get($name)
    {
    $query_props_normalized = static::normalizeAssociative($this->query_props, 'no-cache');
    $cache_attributes_normalized = static::normalizeAssociative($this->cache_attributes, 0);
    $cache_related_normalized = static::normalizeAssociative($this->cache_related, 0);

    /**
    * Handle query props.
    */
    if (in_array($name, array_keys($query_props_normalized))) {
    return call_user_func([$this, $name])->get();
    }

    /**
    * Handle any related models that are cached.
    */
    if (in_array($name, array_keys($cache_related_normalized))) {
    $minutes = (int) $cache_related_normalized[$name];

    return $this->cacheRelated($name, $minutes, function() use($name) {
    return call_user_func([$this, $name])->get();
    });
    }

    /**
    * Handle and get attribute mutators that are cached.
    */
    if (in_array($name, array_keys($cache_attributes_normalized))) {
    $minutes = (int) $cache_attributes_normalized[$name];

    return $this->cacheAttribute($name, $minutes, function() use($name) {
    return $this->getAttribute($name);
    });
    }

    return parent::__get($name);
    }

    /**
    * Build out an associative array with mixed values.
    *
    * e.g.
    *
    * [
    * 'cats',
    * 'dogs' => 1
    * ]
    *
    * becomes
    *
    * [
    * 'cats' => 0,
    * 'dogs' => 1
    * ]
    *
    * @todo Move this method somewhere in a Util class.
    *
    * @param array $arr
    * @param int $default_value
    * @return array
    */
    public static function normalizeAssociative(array $arr, $default_value = 0)
    {
    $normalized = [];

    foreach($arr as $key => $value) {
    $new_key = $key;
    $new_value = $value;
    if (is_int($key)) {
    $new_key = $value;
    $new_value = $default_value;
    }
    $normalized[$new_key] = $new_value;
    }

    return $normalized;
    }

    }
    113 changes: 113 additions & 0 deletions ModelCacheServiceProvider.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    <?php

    namespace App\Providers;

    use App\User;
    use App\Model;
    use App\Models\Listing;
    use App\Models\Legacy\Orders;
    use Illuminate\Support\ServiceProvider;

    /**
    * Class ModelCacheServiceProvider
    */
    class ModelCacheServiceProvider extends ServiceProvider
    {

    /**
    * Automatically clear caches for related data, cached on Parent models.
    *
    * @see Model::$cache_related
    * @var array $uses_cache_related */
    public static $uses_cache_related = [
    // [
    // 'model' => User::class,
    // 'related' => Listing::class,
    // 'foreign_key' => 'user_id',
    // 'methods' => ['listings'],
    // 'events' => ['saved', 'created', 'deleted']
    // ]
    ];

    /**
    * Automatically clear caches for related data, cached on Parent models.
    *
    * @see Model::$cache_related
    * @var array $uses_cache_attributes */
    public static $uses_cache_attributes = [
    // [
    // 'model' => User::class,
    // 'related' => Commission::class,
    // 'foreign_key' => 'user_id',
    // 'attributes' => ['total_owed'],
    // 'events' => ['saved', 'created', 'deleted']
    // ]
    ];

    /**
    * Bootstrap the application services.
    *
    * @return void
    */
    public function boot()
    {

    // For any usages of "uses cache related"
    foreach (static::$uses_cache_related as $purge_event) {

    // And for any of the specified model events
    $events = $purge_event['events'];
    foreach($events as $event) {
    $model = $purge_event['model'];
    $related = $purge_event['related'];
    $foreign_key = $purge_event['foreign_key'];
    $methods = $purge_event['methods'];

    // Add a listener to the related model to purge the parent's key
    call_user_func([$related, $event], function($r) use($model, $foreign_key, $methods) {
    $parent_id = $r->{$foreign_key};

    // To bust the related caches
    foreach ($methods as $method) {
    call_user_func_array([$model, 'cacheBust'], [$model, $parent_id, 'related', $method]);
    }
    });
    }
    }

    // For any usages of "uses cache attributes"
    foreach (static::$uses_cache_attributes as $purge_event) {

    // And for any of the specified model events
    $events = $purge_event['events'];
    foreach($events as $event) {
    $model = $purge_event['model'];
    $related = $purge_event['related'];
    $foreign_key = $purge_event['foreign_key'];
    $attributes = $purge_event['attributes'];

    // Add a listener to the related model to purge the parent's key
    call_user_func([$related, $event], function($r) use($model, $foreign_key, $attributes) {
    $parent_id = $r->{$foreign_key};

    // To bust the related caches
    foreach ($attributes as $attribute) {
    call_user_func_array([$model, 'cacheBust'], [$model, $parent_id, 'attribute', $attribute]);
    }
    });
    }
    }

    }

    /**
    * Register the application services.
    *
    * @return void
    */
    public function register()
    {

    }

    }