Last active
June 5, 2020 02:42
Revisions
-
danrichards revised this gist
Nov 17, 2017 . 1 changed file with 0 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,9 +2,6 @@ namespace App; use App\Utils\Data; use App\Utils\Str; use Cache; -
danrichards revised this gist
Nov 17, 2017 . 1 changed file with 152 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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)) { $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; } } -
danrichards revised this gist
Nov 28, 2016 . No changes.There are no files selected for viewing
-
danrichards revised this gist
Nov 28, 2016 . 1 changed file with 75 additions and 50 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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\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 $attribute * @param null $id * @return string */ private function cacheAttributeKey($attribute, $id = null) { $id = $id ?: $this->getKey(); return implode('|', [get_class($this), $id, 'attribute', $attribute]); } /** * Simple method caching, for more robust caching, use a Repository * * @param string $related * @param null $id * @return string */ private function cacheRelatedKey($related, $id = null) { $id = $id ?: $this->getKey(); 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) { $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) { $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, $id = null) { $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, $id)); } } @@ -144,7 +154,7 @@ public function cacheAttributeBust($attribute = null, $parent_id) public function cacheRelatedBust($related = null) { $related = is_null($related) ? 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 = 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) } /** * @param $query * @param Model $model * @param null $morph * @return \Illuminate\Database\Eloquent\Builder */ 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)); } 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"); } } -
danrichards revised this gist
Sep 21, 2016 . 2 changed files with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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) ? 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) ? static::normalizeAssociative($this->cache_related, 0) : $related; $related = is_string($related) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,8 +4,8 @@ use App\Model; // use App\User; // use App\Models\Listing; // use App\Models\Commission; use Illuminate\Support\ServiceProvider; /** -
danrichards revised this gist
Aug 31, 2016 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,10 +2,10 @@ namespace App\Providers; use App\Model; // use App\User; // use App\Models\Order; // use App\Models\Listing; use Illuminate\Support\ServiceProvider; /** -
danrichards revised this gist
Aug 31, 2016 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,9 +2,8 @@ namespace App; use Cache; use Closure; use Illuminate\Database\Eloquent\Model as BaseModel; /** -
danrichards revised this gist
Aug 31, 2016 . No changes.There are no files selected for viewing
-
danrichards created this gist
Aug 31, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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; } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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() { } }