├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── public └── .gitkeep ├── src ├── Mmanos │ └── Metable │ │ ├── Metable.php │ │ ├── MetableServiceProvider.php │ │ ├── QueryBuilder.php │ │ └── Stubs │ │ ├── MetaModel.stub.php │ │ ├── MetableMigration.stub.php │ │ └── MetasMigration.stub.php ├── commands │ ├── MetableCommand.php │ └── MetasCommand.php ├── config │ └── .gitkeep ├── controllers │ └── .gitkeep ├── lang │ └── .gitkeep ├── migrations │ └── .gitkeep └── views │ └── .gitkeep └── tests └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | Thumbs.db 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Meta Package for Laravel 4 2 | 3 | This package adds meta support to your Laravel application. You can configure it to attach meta to any of your existing Eloquent models. 4 | 5 | ## Installation 6 | 7 | #### Composer 8 | 9 | Add this to your composer.json file, in the require object: 10 | 11 | ```javascript 12 | "mmanos/laravel-metable": "dev-master" 13 | ``` 14 | 15 | After that, run composer install to install the package. 16 | 17 | #### Service Provider 18 | 19 | Register the `Mmanos\Metable\MetableServiceProvider` in your `app` configuration file. 20 | 21 | ## Configuration 22 | 23 | #### Metas Migration and Model 24 | 25 | First you'll need to publish a `metas` table and a `Meta` model. This table will hold a summary of all meta created by your metable models. 26 | 27 | ```console 28 | $ php artisan laravel-metable:metas metas 29 | ``` 30 | 31 | > **Note:** Modify the last parameter of this call to change the table/model name. 32 | 33 | > **Note:** You may publish as many meta tables as you need, if you want to keep the meta separate for different types of content, for example. 34 | 35 | #### Metable Migration 36 | 37 | Next, publish a migration for each type of content you want to attach meta to. You may attach meta to as many types of content as you wish. For example, if you want to be able to add meta to both a `users` table and a `blog_posts` table, run this migration once for each table. 38 | 39 | ```console 40 | $ php artisan laravel-metable:metable user_metas 41 | ``` 42 | 43 | #### Run Migrations 44 | 45 | Once the migration has been created, simply run the `migrate` command. 46 | 47 | #### Model Setup 48 | 49 | Next, add the `Metable` trait to each metable model definition: 50 | 51 | ```php 52 | use Mmanos\Metable\Metable; 53 | 54 | class User extends Eloquent 55 | { 56 | use Metable; 57 | } 58 | ``` 59 | 60 | Then you need to specify the meta model as well as the metable table to use with your model: 61 | 62 | ```php 63 | class User extends Eloquent 64 | { 65 | protected $meta_model = 'Meta'; 66 | protected $metable_table = 'user_metas'; 67 | } 68 | ``` 69 | 70 | #### Syncing Custom Attributes 71 | 72 | Sometimes you will want to have some of the same fields in your content table synced to the metable table records. This will allow you to filter and sort by these attributes when querying the metable table. Luckily this system will automatically sync any fields you define to the metable table records any time there are changes. 73 | 74 | To get started, **modify the metable migration file** to include your additional fields. 75 | 76 | Then, tell your model which fields it needs to sync: 77 | 78 | ```php 79 | class User extends Eloquent 80 | { 81 | protected $metable_table_sync = ['company_id', 'created_at', 'updated_at', 'deleted_at']; 82 | } 83 | ``` 84 | 85 | Now every time you create or update a model, these fields will by synced to all metable table records for the piece of content. 86 | 87 | #### Syncing Deleted Content 88 | 89 | This package will automatically delete all metable table records for a piece of content when that piece of content is deleted. 90 | 91 | If you are using the `SoftDeletingTrait` and you are syncing the `deleted_at` column to your metable table records, this package will automatically soft-delete all metable table records for a piece of content when that piece of content is deleted. If the content is restored, then the metable table records are restored as well. 92 | 93 | ## Working With Meta 94 | 95 | #### Setting Content Meta 96 | 97 | To set a meta value on an existing piece of content: 98 | 99 | ```php 100 | $user->setMeta('employer', 'Company, Inc.'); 101 | ``` 102 | 103 | Or set multiple metas at once: 104 | 105 | ```php 106 | $user->setMeta([ 107 | 'employer' => 'Company, Inc.', 108 | 'employed_for_years' => 10, 109 | ]); 110 | ``` 111 | 112 | > **Note:** If a piece of content already has a meta the existing value will be updated. 113 | 114 | #### Unsetting Content Meta 115 | 116 | Similarly, you may unset meta from an existing piece of content: 117 | 118 | ```php 119 | $user->unsetMeta('employer'); 120 | ``` 121 | 122 | Or unset multiple metas at once: 123 | 124 | ```php 125 | $user->unsetMeta('employer', 'employed_for_years'); 126 | // or 127 | $user->unsetMeta(['employer', 'employed_for_years']); 128 | ``` 129 | 130 | > **Note:** The system will not throw an error if the content does not have the requested meta. 131 | 132 | #### Checking for Metas 133 | 134 | To see if a piece of content has a meta: 135 | 136 | ```php 137 | if ($user->hasMeta('employer')) { 138 | 139 | } 140 | ``` 141 | 142 | #### Retrieving Meta 143 | 144 | To retrieve a meta value on a piece of content, use the `meta` method: 145 | 146 | ```php 147 | $employer = $user->meta('employer'); 148 | ``` 149 | 150 | Or specify a default value, if not set: 151 | 152 | ```php 153 | $employer = $user->meta('employer', 'Unemployed'); 154 | ``` 155 | 156 | You may also retrieve more than one meta at a time and get back an array of values: 157 | 158 | ```php 159 | $employer = $user->meta(['employer', 'employed_for_years']); 160 | ``` 161 | 162 | #### Retrieving All Metas 163 | 164 | To fetch all metas associated with a piece of content, use the `metas` relationship: 165 | 166 | ```php 167 | $metas = $user->metas; 168 | ``` 169 | 170 | #### Retrieving an Array of All Metas 171 | 172 | To fetch all metas associated with a piece of content and return them as an array, use the `metasArray` method: 173 | 174 | ```php 175 | $metas = $user->metasArray(); 176 | ``` 177 | 178 | ## Querying for Content from Meta 179 | 180 | #### Performing Queries 181 | 182 | Now let's say you want to query for all content that has a given meta: 183 | 184 | ```php 185 | $users = User::withMeta('employer')->take(10)->get(); 186 | ``` 187 | 188 | Or optionally specify the meta value to match: 189 | 190 | ```php 191 | $users = User::whereMeta('employer', 'Company, Inc.')->take(10)->get(); 192 | ``` 193 | 194 | Or optionally specify the meta value and operator to match: 195 | 196 | ```php 197 | $users = User::whereMeta('employed_for_years', '>', '5')->take(10)->get(); 198 | ``` 199 | 200 | These queries extend the same `QueryBuilder` class that you are used to working with, so all of those methods work as well: 201 | 202 | ```php 203 | $users = User::whereMeta('employer', 'Company, Inc.') 204 | ->where('meta_created_at', '>', '2015-01-01 00:00:00') 205 | ->with('company') 206 | ->orderBy('meta_created_at', 'desc') 207 | ->paginate(10); 208 | ``` 209 | 210 | > **Note:** The `update` and `delete` methods on a QueryBuilder object do not work for these queries. 211 | 212 | You may query for content that has any of the given meta: 213 | 214 | ```php 215 | $users = User::withAnyMeta('company', 'employed_for_years')->get(); 216 | // or 217 | $users = User::withAnyMeta(['company', 'employed_for_years'])->get(); 218 | ``` 219 | 220 | Or query for content that has any of the given meta that matches the given values: 221 | 222 | ```php 223 | $users = User::whereAnyMeta([ 224 | 'company' => 'Company, Inc.', 225 | 'employed_for_years' => '10' 226 | ])->get(); 227 | ``` 228 | 229 | > **Note:** Query performance can be reduced for the `withAnyMeta` and `whereAnyMeta` queries if your queries match thousands of records or more. 230 | 231 | And you may combine multiple filters: 232 | 233 | ```php 234 | // Fetch all users who have the 'agent' meta and who have 'company' or 'employed_for_years'. 235 | $users = User::whereMeta('agent', '1')->withAnyMeta('company', 'employed_for_years')->get(); 236 | ``` 237 | 238 | #### Meta Contexts 239 | 240 | Sometimes you might want to associate your metas (summary) table records with some custom context for your application. For example, say you have a `companies` table and a `users` table and each user belongs to a company. And now you also want to associate each meta record with a company allowing you to fetch all meta used by each individual company. In order to do so, we have to tell this package to be aware of this company context and modify it's queries accordingly. 241 | 242 | To get started, make sure you **modify your metas migration** to include any context fields (`company_id`, in this case). You might also need to update the unique index, if necessary. 243 | 244 | Then modify your metable model by adding a `metaContext` method: 245 | 246 | ```php 247 | class User extends Eloquent 248 | { 249 | public function metaContext() 250 | { 251 | return $this->company; 252 | } 253 | } 254 | ``` 255 | 256 | Next modify your `Meta` model (or whatever name you specified during configuration) to apply any contexts: 257 | 258 | ```php 259 | class Meta extends Eloquent 260 | { 261 | public static function applyQueryContext($query, $context) 262 | { 263 | $query->where('company_id', $context->id); 264 | } 265 | 266 | public static function applyModelContext($model, $context) 267 | { 268 | $model->company_id = $context->id; 269 | } 270 | } 271 | ``` 272 | 273 | The `applyQueryContext` method will adjust any meta queries used by this package to filter on `company_id`. 274 | 275 | The `applyModelContext` method is called when creating a new `Meta` record and should set any required context fields. 276 | 277 | Finally, when performing queries, specify the context to apply: 278 | 279 | ```php 280 | $users = User::withMeta('employer')->withMetaContext($company)->take(10)->get(); 281 | ``` 282 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mmanos/laravel-metable", 3 | "description": "A meta package for Laravel 4 models.", 4 | "keywords": ["laravel", "meta", "metas", "metable", "model", "eloquent"], 5 | "authors": [ 6 | { 7 | "name": "Mark Manos", 8 | "email": "mark@airpac.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.4.0", 13 | "illuminate/support": "~4.1" 14 | }, 15 | "autoload": { 16 | "classmap": [ 17 | "src/migrations", 18 | "src/commands" 19 | ], 20 | "psr-0": { 21 | "Mmanos\\Metable\\": "src/" 22 | } 23 | }, 24 | "minimum-stability": "stable", 25 | "license": "MIT" 26 | } 27 | -------------------------------------------------------------------------------- /public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-metable/f6a6bdcebcf7eefd42e9d58c37e3206361f147de/public/.gitkeep -------------------------------------------------------------------------------- /src/Mmanos/Metable/Metable.php: -------------------------------------------------------------------------------- 1 | syncMetableTableAttributes(); 19 | }); 20 | 21 | static::deleted(function ($model) { 22 | $model->handleDeletedModelMetas(); 23 | }); 24 | 25 | static::registerModelEvent('restored', function ($model) { 26 | $model->handleRestoredModelMetas(); 27 | }); 28 | } 29 | 30 | /** 31 | * Return the meta class for this model. 32 | * 33 | * @return string 34 | */ 35 | public function metaModel() 36 | { 37 | return $this->meta_model; 38 | } 39 | 40 | /** 41 | * Return the metable table name for this model. 42 | * 43 | * @return string 44 | */ 45 | public function metableTable() 46 | { 47 | return $this->metable_table; 48 | } 49 | 50 | /** 51 | * Define the metas relationship for this model. 52 | * 53 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 54 | */ 55 | public function metas() 56 | { 57 | return $this->belongsToMany($this->metaModel(), $this->metableTable(), 'xref_id')->withPivot('value', 'meta_created_at', 'meta_updated_at'); 58 | } 59 | 60 | /** 61 | * Return an array of all metas associated with this model. 62 | * 63 | * @return array 64 | */ 65 | public function metasArray() 66 | { 67 | $metas = array(); 68 | 69 | foreach ($this->metas as $meta) { 70 | if (!isset($meta->pivot)) { 71 | continue; 72 | } 73 | 74 | $metas[$meta->name] = $meta->pivot->value; 75 | } 76 | 77 | return $metas; 78 | } 79 | 80 | /** 81 | * Retrieve one or more values from the current model. 82 | * Can be either a meta name, a meta model, a collection of models, or an array of models. 83 | * 84 | * @param Eloquent|string|Collection|array $name 85 | * @param mixed $default 86 | * 87 | * @return mixed|array 88 | */ 89 | public function meta($meta, $default = null) 90 | { 91 | $single = ($meta instanceof Collection) ? false : is_array($meta) ? false : true; 92 | $metas = ($meta instanceof Collection) ? $meta->all() : is_array($meta) ? $meta : array($meta); 93 | 94 | $values = array(); 95 | 96 | foreach ($metas as $m) { 97 | if (is_object($m)) { 98 | $values[$m->name] = $this->metas->find($m->id, $default); 99 | continue; 100 | } 101 | 102 | $found = $this->metas->filter(function ($m2) use ($m) { 103 | return $m2->name == $m; 104 | }); 105 | 106 | if (!$found->isEmpty()) { 107 | if (isset($found->first()->pivot)) { 108 | $values[$m] = $found->first()->pivot->value; 109 | continue; 110 | } 111 | } 112 | 113 | $values[$m] = $default; 114 | } 115 | 116 | return $single ? current($values) : $values; 117 | } 118 | 119 | /** 120 | * Returns true if the current model has the given meta name (or meta model). 121 | * 122 | * @param Eloquent|string $meta 123 | * 124 | * @return bool 125 | */ 126 | public function hasMeta($meta) 127 | { 128 | $found = $this->metas->filter(function ($t) use ($meta) { 129 | if (is_object($meta)) { 130 | return $t->id == $meta->id; 131 | } 132 | else { 133 | return $t->name == $meta; 134 | } 135 | }); 136 | 137 | return !$found->isEmpty(); 138 | } 139 | 140 | /** 141 | * Add one or more metas to the current model. 142 | * Can be either a meta name, a meta model, or an array of models. 143 | * 144 | * @param Eloquent|string|array $meta 145 | * @param mixed $value 146 | * 147 | * @return Eloquent 148 | */ 149 | public function setMeta($meta, $value = null) 150 | { 151 | $metas = is_array($meta) ? $meta : array($meta => $value); 152 | 153 | foreach ($metas as $m => $v) { 154 | if (!is_object($m)) { 155 | $m = $this->findMetaByNameOrCreate($m); 156 | } 157 | 158 | if (!$m || !$m instanceof Eloquent) { 159 | continue; 160 | } 161 | 162 | if ($this->hasMeta($m)) { 163 | if (is_null($v)) { 164 | $this->unsetMeta($m); 165 | continue; 166 | } 167 | 168 | $attributes = array( 169 | 'value' => $v, 170 | 'meta_updated_at' => date('Y-m-d H:i:s'), 171 | ); 172 | $this->metas()->updateExistingPivot($m->id, array_merge($this->metableTableSyncAttributes(), $attributes)); 173 | $this->metas->find($m->id)->pivot->value = $v; 174 | $this->metas->find($m->id)->pivot->meta_updated_at = $attributes['meta_updated_at']; 175 | } 176 | else { 177 | if (is_null($v)) { 178 | continue; 179 | } 180 | 181 | $this->metas()->attach($m, array_merge($this->metableTableSyncAttributes(), array( 182 | 'value' => $v, 183 | 'meta_created_at' => date('Y-m-d H:i:s'), 184 | 'meta_updated_at' => date('Y-m-d H:i:s'), 185 | ))); 186 | 187 | $m->increment('num_items'); 188 | 189 | $this->metas->add($m); 190 | } 191 | } 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * Remove one or more metas from the current model. 198 | * Can be either a meta name, a meta model, a collection of models, or an array of models. 199 | * Will remove all metas if no paramter is passed. 200 | * 201 | * @param Eloquent|string|Collection|array $meta 202 | * 203 | * @return Eloquent 204 | */ 205 | public function unsetMeta($meta = null) 206 | { 207 | $args = func_get_args(); 208 | 209 | if (0 == count($args)) { 210 | $args[] = $this->metas; 211 | } 212 | 213 | foreach ($args as $arg) { 214 | $metas = ($arg instanceof Collection) ? $arg->all() : is_array($arg) ? $arg : array($arg); 215 | 216 | foreach ($metas as $m) { 217 | if (!is_object($m)) { 218 | $m = $this->findMetaByName($m); 219 | } 220 | 221 | if (!$m || !$m instanceof Eloquent) { 222 | continue; 223 | } 224 | 225 | if (!$this->hasMeta($m)) { 226 | return; 227 | } 228 | 229 | $this->metas()->detach($m); 230 | 231 | $m->decrement('num_items'); 232 | 233 | foreach ($this->metas as $idx => $cur_meta) { 234 | if ($cur_meta->getKey() == $m->getKey()) { 235 | $this->metas->pull($idx); 236 | break; 237 | } 238 | } 239 | } 240 | } 241 | 242 | return $this; 243 | } 244 | 245 | /** 246 | * Return a new meta table query. 247 | * 248 | * @return \Illuminate\Database\Eloquent\Builder 249 | */ 250 | private function newMetaQuery() 251 | { 252 | $meta_model = $this->metaModel(); 253 | $meta_instance = new $meta_model; 254 | 255 | $query = $meta_instance->newQuery(); 256 | 257 | if (method_exists($this, 'metaContext')) { 258 | if (method_exists($meta_model, 'applyQueryContext')) { 259 | call_user_func_array( 260 | array($meta_model, 'applyQueryContext'), 261 | array($query, $this->metaContext()) 262 | ); 263 | } 264 | } 265 | 266 | return $query; 267 | } 268 | 269 | /** 270 | * Find a meta from the given name. 271 | * 272 | * @param string $name 273 | * 274 | * @return Eloquent|null 275 | */ 276 | private function findMetaByName($name) 277 | { 278 | return $this->newMetaQuery()->where('name', $name)->first(); 279 | } 280 | 281 | /** 282 | * Find a meta from the given name or create it if not found. 283 | * 284 | * @param string $name 285 | * 286 | * @return Eloquent 287 | */ 288 | private function findMetaByNameOrCreate($name) 289 | { 290 | if ($meta = $this->findMetaByName($name)) { 291 | return $meta; 292 | } 293 | 294 | $meta_model = $this->metaModel(); 295 | 296 | $meta = new $meta_model; 297 | $meta->name = $name; 298 | $meta->num_items = 0; 299 | 300 | if (method_exists($this, 'metaContext')) { 301 | if (method_exists($meta_model, 'applyModelContext')) { 302 | call_user_func_array( 303 | array($meta_model, 'applyModelContext'), 304 | array($meta, $this->metaContext()) 305 | ); 306 | } 307 | } 308 | 309 | $meta->save(); 310 | 311 | return $meta; 312 | } 313 | 314 | /** 315 | * Return an array of model attributes to sync on the metable_table records. 316 | * 317 | * @return array 318 | */ 319 | private function metableTableSyncAttributes() 320 | { 321 | if (!isset($this->metable_table_sync)) { 322 | return array(); 323 | } 324 | 325 | $attributes = array(); 326 | foreach ($this->metable_table_sync as $attr) { 327 | $attributes[$attr] = $this->getAttribute($attr); 328 | } 329 | 330 | return $attributes; 331 | } 332 | 333 | /** 334 | * Returns whether or not we are soft-deleting metable table records. 335 | * 336 | * @return bool 337 | */ 338 | public function metableTableSoftDeletes() 339 | { 340 | if (method_exists($this, 'getDeletedAtColumn')) { 341 | if (array_key_exists($this->getDeletedAtColumn(), $this->metableTableSyncAttributes())) { 342 | return true; 343 | } 344 | } 345 | 346 | return false; 347 | } 348 | 349 | /** 350 | * Sync metable table attributes to all metas associated with this model. 351 | * 352 | * @return void 353 | */ 354 | public function syncMetableTableAttributes() 355 | { 356 | if (empty($this->metableTableSyncAttributes())) { 357 | return; 358 | } 359 | 360 | DB::table($this->metableTable()) 361 | ->where('xref_id', $this->getKey()) 362 | ->update($this->metableTableSyncAttributes()); 363 | } 364 | 365 | /** 366 | * Delete metable table records for this current model since it was just deleted. 367 | * 368 | * @return void 369 | */ 370 | private function handleDeletedModelMetas() 371 | { 372 | if ($this->metableTableSoftDeletes()) { 373 | foreach ($this->metas as $meta) { 374 | $this->syncMetableTableAttributes(); 375 | $meta->decrement('num_items'); 376 | } 377 | 378 | return; 379 | } 380 | 381 | $this->unsetMeta(); 382 | } 383 | 384 | /** 385 | * Restore metable table records for this current model since it was just restorede. 386 | * 387 | * @return void 388 | */ 389 | private function handleRestoredModelMetas() 390 | { 391 | if (!$this->metableTableSoftDeletes()) { 392 | return; 393 | } 394 | 395 | foreach ($this->metas as $meta) { 396 | $this->syncMetableTableAttributes(); 397 | $meta->increment('num_items'); 398 | } 399 | } 400 | 401 | /** 402 | * Begin querying the model's metable table and filter on the given meta name(s) or meta model(s). 403 | * 404 | * @param Eloquent|string|Collection|array $meta 405 | * 406 | * @return QueryBuilder 407 | */ 408 | public static function withMeta($meta) 409 | { 410 | return call_user_func_array(array(static::queryMetas(), 'withMeta'), func_get_args()); 411 | } 412 | 413 | /** 414 | * Begin querying the model's metable table and filter on the given meta id(s). 415 | * 416 | * @param int|array $id 417 | * 418 | * @return QueryBuilder 419 | */ 420 | public static function withMetaId($id) 421 | { 422 | return call_user_func_array(array(static::queryMetas(), 'withMetaId'), func_get_args()); 423 | } 424 | 425 | /** 426 | * Begin querying the model's metable table and filter on any of the given meta name(s) or meta model(s). 427 | * 428 | * @param Eloquent|string|Collection|array $meta 429 | * 430 | * @return QueryBuilder 431 | */ 432 | public static function withAnyMeta($meta) 433 | { 434 | return call_user_func_array(array(static::queryMetas(), 'withAnyMeta'), func_get_args()); 435 | } 436 | 437 | /** 438 | * Begin querying the model's metable table and filter on any of the given meta id(s). 439 | * 440 | * @param int|array $id 441 | * 442 | * @return QueryBuilder 443 | */ 444 | public static function withAnyMetaId($id) 445 | { 446 | return call_user_func_array(array(static::queryMetas(), 'withAnyMetaId'), func_get_args()); 447 | } 448 | 449 | /** 450 | * Begin querying the model's metable table and filter on the given meta name(s) (or meta model(s)) and meta value(s). 451 | * 452 | * @param Eloquent|string|array $meta 453 | * @param string $operator 454 | * @param mixed $value 455 | * 456 | * @return QueryBuilder 457 | */ 458 | public static function whereMeta($meta, $operator = null, $value = null) 459 | { 460 | return call_user_func_array(array(static::queryMetas(), 'whereMeta'), func_get_args()); 461 | } 462 | 463 | /** 464 | * Begin querying the model's metable table and filter on the given meta id(s) and meta value(s). 465 | * 466 | * @param int|array $id 467 | * @param string $operator 468 | * @param mixed $value 469 | * 470 | * @return QueryBuilder 471 | */ 472 | public static function whereMetaId($id, $operator = null, $value = null) 473 | { 474 | return call_user_func_array(array(static::queryMetas(), 'whereMetaId'), func_get_args()); 475 | } 476 | 477 | /** 478 | * Begin querying the model's metable table and filter on any of the given meta names (or meta models) and meta values. 479 | * 480 | * @param array $metas 481 | * 482 | * @return QueryBuilder 483 | */ 484 | public static function whereAnyMeta(array $metas) 485 | { 486 | return call_user_func_array(array(static::queryMetas(), 'whereAnyMeta'), func_get_args()); 487 | } 488 | 489 | /** 490 | * Begin querying the model's metable table and filter on any of the given meta ids and meta values. 491 | * 492 | * @param array $metas 493 | * 494 | * @return QueryBuilder 495 | */ 496 | public static function whereAnyMetaId(array $metas) 497 | { 498 | return call_user_func_array(array(static::queryMetas(), 'whereAnyMetaId'), func_get_args()); 499 | } 500 | 501 | /** 502 | * Begin querying the model's metable table. 503 | * 504 | * @return QueryBuilder 505 | */ 506 | public static function queryMetas() 507 | { 508 | $model = new static; 509 | 510 | $conn = $model->getConnection(); 511 | $query = new QueryBuilder( 512 | $conn, 513 | $conn->getQueryGrammar(), 514 | $conn->getPostProcessor() 515 | ); 516 | 517 | $query->setModel($model); 518 | 519 | return $query; 520 | } 521 | } 522 | -------------------------------------------------------------------------------- /src/Mmanos/Metable/MetableServiceProvider.php: -------------------------------------------------------------------------------- 1 | package('mmanos/laravel-metable'); 22 | } 23 | 24 | /** 25 | * Register the service provider. 26 | * 27 | * @return void 28 | */ 29 | public function register() 30 | { 31 | $this->app->bindShared('command.laravel-metable.metas', function ($app) { 32 | return new MetasCommand; 33 | }); 34 | $this->commands('command.laravel-metable.metas'); 35 | 36 | $this->app->bindShared('command.laravel-metable.metable', function ($app) { 37 | return new MetableCommand; 38 | }); 39 | $this->commands('command.laravel-metable.metable'); 40 | } 41 | 42 | /** 43 | * Get the services provided by the provider. 44 | * 45 | * @return array 46 | */ 47 | public function provides() 48 | { 49 | return array(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Mmanos/Metable/QueryBuilder.php: -------------------------------------------------------------------------------- 1 | model = $model; 26 | 27 | $this->from($this->model->metableTable() . ' AS m'); 28 | 29 | if ($this->model->metableTableSoftDeletes()) { 30 | $this->whereNull('m.' . $this->model->getDeletedAtColumn()); 31 | } 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Set the meta query context to be used by this instance. 38 | * 39 | * @param mixed $meta_context 40 | * 41 | * @return QueryBuilder 42 | */ 43 | public function withMetaContext($meta_context) 44 | { 45 | $this->meta_context = $meta_context; 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * Filter the query on the given meta name(s) or meta model(s). 52 | * 53 | * @param Eloquent|string|Collection|array $meta 54 | * 55 | * @return QueryBuilder 56 | */ 57 | public function withMeta($meta) 58 | { 59 | foreach (func_get_args() as $arg) { 60 | $metas = ($arg instanceof Collection) ? $arg->all() : is_array($arg) ? $arg : array($arg); 61 | 62 | foreach ($metas as $m) { 63 | if (is_object($m)) { 64 | $this->filters[]['metas'][] = array('id' => $m->id); 65 | } 66 | else { 67 | $this->filters[]['metas'][] = array('name' => $m); 68 | } 69 | } 70 | } 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Filter the query on the given meta id(s). 77 | * 78 | * @param int|array $id 79 | * 80 | * @return QueryBuilder 81 | */ 82 | public function withMetaId($id) 83 | { 84 | foreach (func_get_args() as $arg) { 85 | $meta_ids = (array) $arg; 86 | 87 | foreach ($meta_ids as $meta_id) { 88 | $this->filters[]['metas'][] = array('id' => $meta_id); 89 | } 90 | } 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * Filter the query on any of the given meta name(s) or meta model(s). 97 | * 98 | * @param Eloquent|string|Collection|array $meta 99 | * 100 | * @return QueryBuilder 101 | */ 102 | public function withAnyMeta($meta) 103 | { 104 | $filters = array(); 105 | 106 | foreach (func_get_args() as $arg) { 107 | $metas = ($arg instanceof Collection) ? $arg->all() : is_array($arg) ? $arg : array($arg); 108 | 109 | foreach ($metas as $m) { 110 | if (is_object($m)) { 111 | $filters[] = array('id' => $m->id); 112 | } 113 | else { 114 | $filters[] = array('name' => $m); 115 | } 116 | } 117 | } 118 | 119 | $this->filters[]['metas'] = $filters; 120 | 121 | return $this; 122 | } 123 | 124 | /** 125 | * Filter the query on any of the given meta id(s). 126 | * 127 | * @param int|array $id 128 | * 129 | * @return QueryBuilder 130 | */ 131 | public function withAnyMetaId($id) 132 | { 133 | $filters = array(); 134 | 135 | foreach (func_get_args() as $arg) { 136 | $arg = (array) $arg; 137 | 138 | foreach ($arg as $meta_id) { 139 | $filters[] = array('id' => $m->id); 140 | } 141 | } 142 | 143 | $this->filters[]['metas'] = $filters; 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Filter the query on the given meta name(s) (or meta model(s)) and meta value(s). 150 | * 151 | * @param Eloquent|string|array $meta 152 | * @param string $operator 153 | * @param mixed $value 154 | * 155 | * @return QueryBuilder 156 | */ 157 | public function whereMeta($meta, $operator = null, $value = null) 158 | { 159 | if (null === $value) { 160 | $value = $operator; 161 | $operator = '='; 162 | } 163 | 164 | $metas = is_array($meta) ? $meta : array($meta => array($value, $operator)); 165 | 166 | foreach ($metas as $m => $d) { 167 | $v = is_array($d) ? Arr::get($d, 0) : $d; 168 | $o = is_array($d) ? Arr::get($d, 1, '=') : '='; 169 | 170 | if (is_object($m)) { 171 | $this->filters[]['metas'][] = array('id' => $m->id, 'value' => $v, 'operator' => $o); 172 | } 173 | else { 174 | $this->filters[]['metas'][] = array('name' => $m, 'value' => $v, 'operator' => $o); 175 | } 176 | } 177 | 178 | return $this; 179 | } 180 | 181 | /** 182 | * Filter the query on the given meta id(s). 183 | * 184 | * @param int|array $id 185 | * @param string $operator 186 | * @param mixed $value 187 | * 188 | * @return QueryBuilder 189 | */ 190 | public function whereMetaId($id, $operator = null, $value = null) 191 | { 192 | if (null === $value) { 193 | $value = $operator; 194 | $operator = '='; 195 | } 196 | 197 | $meta_ids = is_array($id) ? $id : array($id => array($value, $operator)); 198 | 199 | foreach ($meta_ids as $m => $d) { 200 | $v = is_array($d) ? Arr::get($d, 0) : $d; 201 | $o = is_array($d) ? Arr::get($d, 1, '=') : '='; 202 | 203 | $this->filters[]['metas'][] = array('id' => $m, 'value' => $v, 'operator' => $o); 204 | } 205 | 206 | return $this; 207 | } 208 | 209 | /** 210 | * Filter the query on any of the given meta names (or meta models) and meta values. 211 | * 212 | * @param array $metas 213 | * 214 | * @return QueryBuilder 215 | */ 216 | public function whereAnyMeta(array $metas) 217 | { 218 | $filters = array(); 219 | 220 | foreach ($metas as $meta => $data) { 221 | $value = is_array($data) ? Arr::get($data, 0) : $data; 222 | $operator = is_array($data) ? Arr::get($data, 1, '=') : '='; 223 | 224 | if (is_object($meta)) { 225 | $filters[] = array('id' => $meta->id, 'value' => $value, 'operator' => $operator); 226 | } 227 | else { 228 | $filters[] = array('name' => $meta, 'value' => $value, 'operator' => $operator); 229 | } 230 | } 231 | 232 | $this->filters[]['metas'] = $filters; 233 | 234 | return $this; 235 | } 236 | 237 | /** 238 | * Filter the query on any of the given meta ids and meta values. 239 | * 240 | * @param array $metas 241 | * 242 | * @return QueryBuilder 243 | */ 244 | public function whereAnyMetaId(array $metas) 245 | { 246 | $filters = array(); 247 | 248 | foreach ($metas as $meta => $data) { 249 | $value = is_array($data) ? Arr::get($data, 0) : $data; 250 | $operator = is_array($data) ? Arr::get($data, 1, '=') : '='; 251 | 252 | $filters[] = array('id' => $meta, 'value' => $value, 'operator' => $operator); 253 | } 254 | 255 | $this->filters[]['metas'] = $filters; 256 | 257 | return $this; 258 | } 259 | 260 | /** 261 | * Set the relationships that should be eager loaded. 262 | * 263 | * @param mixed $relations 264 | * @return QueryBuilder 265 | */ 266 | public function with($relations) 267 | { 268 | if (is_string($relations)) $relations = func_get_args(); 269 | 270 | $this->relations = array_merge($this->relations, $relations); 271 | 272 | return $this; 273 | } 274 | 275 | /** 276 | * Apply any meta filters added to this query. 277 | * Will return false if any requested meta does not exist. 278 | * 279 | * @return bool 280 | */ 281 | protected function applyMetaFilters() 282 | { 283 | if (isset($this->meta_filters_applied)) { 284 | return $this->meta_filters_applied; 285 | } 286 | 287 | if (empty($this->filters)) { 288 | return $this->meta_filters_applied = false; 289 | } 290 | 291 | $meta_model = $this->model->metaModel(); 292 | $meta_instance = new $meta_model; 293 | $meta_query = $meta_instance->newQuery(); 294 | 295 | if (isset($this->meta_context)) { 296 | if (method_exists($meta_model, 'applyQueryContext')) { 297 | call_user_func_array(array($meta_model, 'applyQueryContext'), array($meta_query, $this->meta_context)); 298 | } 299 | } 300 | 301 | $filters = $this->filters; 302 | $meta_query->where(function ($query) use ($filters) { 303 | foreach ($filters as $filter) { 304 | foreach ($filter['metas'] as $sub_filter) { 305 | if (isset($sub_filter['id'])) { 306 | $query->orWhere('id', $sub_filter['id']); 307 | } 308 | else if (isset($sub_filter['name'])) { 309 | $query->orWhere('name', $sub_filter['name']); 310 | } 311 | } 312 | } 313 | }); 314 | 315 | $metas = $meta_query->get(); 316 | $meta_items = $metas->lists('num_items', 'id'); 317 | 318 | $found = array( 319 | 'id' => $metas->lists('id', 'id'), 320 | 'name' => $metas->lists('id', 'name'), 321 | ); 322 | 323 | foreach ($filters as &$filter) { 324 | $found_one = false; 325 | 326 | foreach ($filter['metas'] as $idx => &$sub_filter) { 327 | $key = isset($sub_filter['id']) ? 'id' : 'name'; 328 | $key_value = $sub_filter[$key]; 329 | 330 | if (!array_key_exists($key_value, $found[$key])) { 331 | unset($filter['metas'][$idx]); 332 | continue; 333 | } 334 | 335 | $found_one = true; 336 | $sub_filter['id'] = $found[$key][$key_value]; 337 | 338 | $num_items = $meta_items[$found[$key][$key_value]]; 339 | if (!isset($filter['num_items']) || $num_items > $filter['num_items']) { 340 | $filter['num_items'] = $num_items; 341 | } 342 | } 343 | 344 | if (!$found_one) { 345 | return $this->meta_filters_applied = false; 346 | } 347 | 348 | if (count($filter['metas']) > 1) { 349 | $filter['num_items'] += 100000000; 350 | } 351 | } 352 | 353 | usort($filters, function ($a, $b) { 354 | return ($a['num_items'] < $b['num_items']) ? -1 : 1; 355 | }); 356 | 357 | $set_distinct = false; 358 | 359 | $first_filter = current(array_splice($filters, 0, 1)); 360 | if (count($first_filter['metas']) > 1) { 361 | $this->where(function ($query) use ($first_filter) { 362 | foreach ($first_filter['metas'] as $f) { 363 | if (isset($f['value'])) { 364 | $query->orWhere(function ($query) use ($f) { 365 | $query->where('m.meta_id', $f['id']); 366 | $query->where('m.value', $f['operator'], $f['value']); 367 | }); 368 | } 369 | else { 370 | $query->orWhere('m.meta_id', $f['id']); 371 | } 372 | } 373 | }); 374 | 375 | if (!$set_distinct) { 376 | $this->distinct(); 377 | $set_distinct = true; 378 | } 379 | } 380 | else { 381 | foreach ($first_filter['metas'] as $f) { 382 | $this->where('m.meta_id', $f['id']); 383 | if (isset($f['value'])) { 384 | $this->where('m.value', $f['operator'], $f['value']); 385 | } 386 | } 387 | } 388 | 389 | foreach ($filters as $i => $cur_filter) { 390 | $this->join("{$this->model->metableTable()} AS m{$i}", function ($join) use ($i, $cur_filter) { 391 | $join_query = $join->on('m.xref_id', '=', "m{$i}.xref_id"); 392 | 393 | if (count($cur_filter['metas']) <= 1) { 394 | foreach ($cur_filter['metas'] as $f) { 395 | if (isset($f['value'])) { 396 | $join_query->where("m{$i}.meta_id", '=', $f['id']); 397 | $join_query->where("m{$i}.value", $f['operator'], $f['value']); 398 | } 399 | else { 400 | $join_query->where("m{$i}.meta_id", '=', $f['id']); 401 | } 402 | } 403 | } 404 | }); 405 | 406 | if (count($cur_filter['metas']) > 1) { 407 | $this->where(function ($query) use ($cur_filter, $i) { 408 | foreach ($cur_filter['metas'] as $f) { 409 | if (isset($f['value'])) { 410 | $query->orWhere(function ($query2) use ($f, $i) { 411 | $query2->where("m{$i}.meta_id", $f['id']); 412 | $query2->where("m{$i}.value", $f['operator'], $f['value']); 413 | }); 414 | } 415 | else { 416 | $query->orWhere("m{$i}.meta_id", $f['id']); 417 | } 418 | } 419 | }); 420 | 421 | if (!$set_distinct) { 422 | $this->distinct(); 423 | $set_distinct = true; 424 | } 425 | } 426 | } 427 | 428 | return $this->meta_filters_applied = true; 429 | } 430 | 431 | /** 432 | * Execute the query as a "select" statement. 433 | * 434 | * @param array $columns 435 | * @return \Illuminate\Database\Eloquent\Collection|static[] 436 | */ 437 | public function get($columns = array('*')) 438 | { 439 | if (!$this->applyMetaFilters()) { 440 | if (!empty($this->aggregate)) { 441 | return 0; 442 | } 443 | return $this->model->newCollection(); 444 | } 445 | 446 | if (!empty($this->aggregate)) { 447 | return parent::get($columns); 448 | } 449 | else { 450 | $results = parent::get(array('m.xref_id')); 451 | } 452 | 453 | if (empty($results)) { 454 | return $this->model->newCollection(); 455 | } 456 | 457 | $xref_ids = array(); 458 | foreach ($results as $result) { 459 | if (!isset($result->xref_id)) continue; 460 | $xref_ids[] = $result->xref_id; 461 | } 462 | 463 | if (empty($xref_ids)) { 464 | return $this->model->newCollection(); 465 | } 466 | 467 | $key = $this->model->getKeyName(); 468 | 469 | $models = $this->model->newQuery()->whereIn($key, $xref_ids)->get(); 470 | 471 | $models->sortBy(function ($model) use ($xref_ids, $key) { 472 | foreach ($xref_ids as $idx => $i) { 473 | if ($model->{$key} == $i) { 474 | return $idx; 475 | } 476 | } 477 | return 0; 478 | }); 479 | 480 | if (!empty($this->relations)) { 481 | $models->load($this->relations); 482 | } 483 | 484 | return $models; 485 | } 486 | 487 | /** 488 | * Execute the query and get the first result. 489 | * 490 | * @param array $columns 491 | * @return mixed|\Illuminate\Database\Eloquent\Collection|static 492 | */ 493 | public function first($columns = array('*')) 494 | { 495 | $results = $this->take(1)->get($columns); 496 | 497 | return count($results) > 0 ? $results->first() : null; 498 | } 499 | 500 | /** 501 | * Get a paginator for the "select" statement. 502 | * 503 | * @param int $perPage 504 | * @param array $columns 505 | * @return \Illuminate\Pagination\Paginator 506 | */ 507 | public function paginate($perPage = null, $columns = array('*')) 508 | { 509 | $perPage = $perPage ?: $this->model->getPerPage(); 510 | 511 | $paginator = $this->connection->getPaginator(); 512 | 513 | $total = $this->getPaginationCount(); 514 | 515 | return $paginator->make($this->get($columns)->all(), $total, $perPage); 516 | } 517 | 518 | /** 519 | * Update a record in the database. 520 | * 521 | * @param array $values 522 | * @return bool 523 | */ 524 | public function update(array $values) 525 | { 526 | return false; 527 | } 528 | 529 | /** 530 | * Delete a record from the database. 531 | * 532 | * @param mixed $id 533 | * @return bool 534 | */ 535 | public function delete($id = null) 536 | { 537 | return false; 538 | } 539 | } 540 | -------------------------------------------------------------------------------- /src/Mmanos/Metable/Stubs/MetaModel.stub.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('xref_id')->unsigned(); 19 | $table->integer('meta_id')->unsigned(); 20 | $table->text('value'); 21 | $table->timestamp('meta_created_at'); 22 | $table->timestamp('meta_updated_at'); 23 | 24 | $table->unique(array('xref_id', 'meta_id'), 'xref_meta'); 25 | 26 | if ('mysql' != DB::connection()->getDriverName()) { 27 | $table->index(array('meta_id', 'value', 'xref_id'), 'meta_value'); 28 | } 29 | }); 30 | 31 | if ('mysql' == DB::connection()->getDriverName()) { 32 | DB::statement('CREATE INDEX meta_value ON {{table}} (meta_id, value(255), xref_id);'); 33 | } 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function down() 42 | { 43 | Schema::drop('{{table}}'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Mmanos/Metable/Stubs/MetasMigration.stub.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->integer('num_items')->default(0); 20 | $table->timestamps(); 21 | $table->softDeletes(); 22 | 23 | $table->unique('name', 'idx_name'); 24 | $table->index(array('created_at', 'deleted_at', 'num_items'), 'newest_metas'); 25 | $table->index(array('updated_at', 'deleted_at', 'num_items', 'created_at'), 'updated_metas'); 26 | $table->index(array('name', 'deleted_at', 'num_items', 'created_at'), 'alpha_metas'); 27 | $table->index(array('num_items', 'deleted_at', 'created_at'), 'popular_metas'); 28 | }); 29 | } 30 | 31 | /** 32 | * Reverse the migrations. 33 | * 34 | * @return void 35 | */ 36 | public function down() 37 | { 38 | Schema::drop('{{table}}'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/commands/MetableCommand.php: -------------------------------------------------------------------------------- 1 | createBaseMigration(); 32 | file_put_contents($full_migration_path, $this->getMigrationStub()); 33 | $this->info('Migration created successfully!'); 34 | 35 | $this->call('dump-autoload'); 36 | } 37 | 38 | /** 39 | * Create a base migration file. 40 | * 41 | * @return string 42 | */ 43 | protected function createBaseMigration() 44 | { 45 | $name = 'create_' . $this->argument('table') . '_table'; 46 | 47 | $path = $this->laravel['path'].'/database/migrations'; 48 | 49 | return $this->laravel['migration.creator']->create($name, $path); 50 | } 51 | 52 | /** 53 | * Get the contents of the migration stub. 54 | * 55 | * @return string 56 | */ 57 | protected function getMigrationStub() 58 | { 59 | $stub = file_get_contents(__DIR__.'/../Mmanos/Metable/Stubs/MetableMigration.stub.php'); 60 | 61 | $stub = str_replace('{{table}}', $this->argument('table'), $stub); 62 | $stub = str_replace( 63 | '{{class}}', 64 | 'Create' . Str::studly($this->argument('table')) . 'Table', 65 | $stub 66 | ); 67 | 68 | return $stub; 69 | } 70 | 71 | /** 72 | * Get the console command arguments. 73 | * 74 | * @return array 75 | */ 76 | protected function getArguments() 77 | { 78 | return array( 79 | array('table', InputArgument::REQUIRED, 'The name for your metable content table.'), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/commands/MetasCommand.php: -------------------------------------------------------------------------------- 1 | createBaseMigration(); 33 | file_put_contents($full_migration_path, $this->getMigrationStub()); 34 | $this->info('Migration created successfully!'); 35 | 36 | $full_model_path = $this->createBaseModel(); 37 | file_put_contents($full_model_path, $this->getModelStub()); 38 | $this->info('Model created successfully!'); 39 | 40 | $this->call('dump-autoload'); 41 | } 42 | 43 | /** 44 | * Return the name of the table to create. 45 | * 46 | * @return string 47 | */ 48 | protected function tableName() 49 | { 50 | return $this->argument('table') ?: 'metas'; 51 | } 52 | 53 | /** 54 | * Create a base migration file. 55 | * 56 | * @return string 57 | */ 58 | protected function createBaseMigration() 59 | { 60 | $name = 'create_' . $this->tableName() . '_table'; 61 | 62 | $path = $this->laravel['path'].'/database/migrations'; 63 | 64 | return $this->laravel['migration.creator']->create($name, $path); 65 | } 66 | 67 | /** 68 | * Get the contents of the migration stub. 69 | * 70 | * @return string 71 | */ 72 | protected function getMigrationStub() 73 | { 74 | $stub = file_get_contents(__DIR__.'/../Mmanos/Metable/Stubs/MetasMigration.stub.php'); 75 | 76 | $stub = str_replace('{{table}}', $this->tableName(), $stub); 77 | $stub = str_replace( 78 | '{{class}}', 79 | 'Create' . Str::studly($this->tableName()) . 'Table', 80 | $stub 81 | ); 82 | 83 | return $stub; 84 | } 85 | 86 | /** 87 | * Create a base model file. 88 | * 89 | * @return string 90 | */ 91 | protected function createBaseModel() 92 | { 93 | $name_parts = explode('_', $this->tableName()); 94 | 95 | $path = $this->laravel['path'].'/models'; 96 | 97 | for ($i = 0; $i < (count($name_parts) - 1); $i++) { 98 | $path .= '/' . Str::studly(Str::singular($name_parts[$i])); 99 | } 100 | 101 | if (count($name_parts) > 1 && !File::exists($path)) { 102 | File::makeDirectory($path, 0755, true); 103 | } 104 | 105 | $path .= '/' . Str::studly(Str::singular(end($name_parts))) . '.php'; 106 | 107 | return $path; 108 | } 109 | 110 | /** 111 | * Get the contents of the model stub. 112 | * 113 | * @return string 114 | */ 115 | protected function getModelStub() 116 | { 117 | $stub = file_get_contents(__DIR__.'/../Mmanos/Metable/Stubs/MetaModel.stub.php'); 118 | 119 | $name_parts = explode('_', $this->tableName()); 120 | $namespace = ''; 121 | for ($i = 0; $i < (count($name_parts) - 1); $i++) { 122 | $namespace .= '\\' . Str::studly(Str::singular($name_parts[$i])); 123 | } 124 | $namespace = trim($namespace, '\\'); 125 | $class = Str::studly(Str::singular(end($name_parts))); 126 | 127 | $stub = str_replace('{{namespace}}', empty($namespace) ? '' : " namespace {$namespace};", $stub); 128 | $stub = str_replace('{{class}}', $class, $stub); 129 | $stub = str_replace('{{table}}', $this->tableName(), $stub); 130 | 131 | return $stub; 132 | } 133 | 134 | /** 135 | * Get the console command arguments. 136 | * 137 | * @return array 138 | */ 139 | protected function getArguments() 140 | { 141 | return array( 142 | array('table', InputArgument::OPTIONAL, 'The name of your metas table (default is metas).'), 143 | ); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-metable/f6a6bdcebcf7eefd42e9d58c37e3206361f147de/src/config/.gitkeep -------------------------------------------------------------------------------- /src/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-metable/f6a6bdcebcf7eefd42e9d58c37e3206361f147de/src/controllers/.gitkeep -------------------------------------------------------------------------------- /src/lang/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-metable/f6a6bdcebcf7eefd42e9d58c37e3206361f147de/src/lang/.gitkeep -------------------------------------------------------------------------------- /src/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-metable/f6a6bdcebcf7eefd42e9d58c37e3206361f147de/src/migrations/.gitkeep -------------------------------------------------------------------------------- /src/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-metable/f6a6bdcebcf7eefd42e9d58c37e3206361f147de/src/views/.gitkeep -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-metable/f6a6bdcebcf7eefd42e9d58c37e3206361f147de/tests/.gitkeep --------------------------------------------------------------------------------