diff --git a/docs/guide/crud/list.md b/docs/guide/crud/list.md index 5b8cd6b2..8db80871 100644 --- a/docs/guide/crud/list.md +++ b/docs/guide/crud/list.md @@ -149,6 +149,7 @@ See all supported field properties : | **source** | `string` | Resource property to display. | | **type** | `string` | Type of [field](../components/fields.md) to use. | | **label** | `string` | Column title header, use [localized property source](../i18n.md) by default. | +| **labelKey** | `string` | Override default source as i18n key message | | **sortable** | `boolean` | Activate server-side sort. | | **align** | `string` | You can Use `left`, `right`, `center` for each cell `align` attribute. | | **link** | `string` | Use any valid `show` or `edit` action if you want to wrap field inside resource action link. | @@ -327,6 +328,7 @@ See all supported field properties : | **source** | `string` | Resource property to display. | | **type** | `string` | Type of [input](../components/inputs.md) to use. | | **label** | `string` | Column title header, use [localized property source](../i18n.md) by default. | +| **labelKey** | `string` | Override default source as i18n key message | | **alwaysOn** | `boolean` | Keep filter always active and visible. Not removable. | | **attributes** | `object` | All props or attributes to merge to the [input component](../components/inputs.md). | diff --git a/examples/api-platform/vue.config.js b/examples/api-platform/vue.config.js index 18b2d8de..ff604461 100644 --- a/examples/api-platform/vue.config.js +++ b/examples/api-platform/vue.config.js @@ -1,5 +1,5 @@ module.exports = { - transpileDependencies: ["vuetify", "vtec-admin"], + transpileDependencies: ["vuetify"], pluginOptions: { i18n: { locale: "en", diff --git a/examples/demo-laravel/app/Book.php b/examples/demo-laravel/app/Book.php index c564f0a1..189c5a8a 100644 --- a/examples/demo-laravel/app/Book.php +++ b/examples/demo-laravel/app/Book.php @@ -16,21 +16,25 @@ * * @property int $id * @property int $publisher_id + * @property int $category_id * @property string|null $isbn * @property string $title * @property string $description * @property string $summary - * @property string $category * @property array $formats * @property float $price * @property bool $commentable * @property array $tags * @property Carbon $publication_date + * @property-read Category $category * @property-read \Illuminate\Database\Eloquent\Collection|\App\Review[] $reviews * @property-read int|null $reviews_count * @property-read \App\Publisher $publisher * @property-read \Illuminate\Database\Eloquent\Collection|\App\Author[] $authors * @property-read int|null $authors_count + * @property-read mixed $translations + * @property-read \Illuminate\Database\Eloquent\Collection|Media[] $media + * @property-read int|null $media_count * @method static \Illuminate\Database\Eloquent\Builder|\App\Book newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|\App\Book newQuery() * @method static \Illuminate\Database\Eloquent\Builder|\App\Book query() @@ -40,9 +44,6 @@ * @method static \Illuminate\Database\Eloquent\Builder|\App\Book pricierThan($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Book commentables() * @mixin \Eloquent - * @property-read mixed $translations - * @property-read \Illuminate\Database\Eloquent\Collection|Media[] $media - * @property-read int|null $media_count */ class Book extends Model implements HasMedia { @@ -58,7 +59,7 @@ class Book extends Model implements HasMedia 'description', 'summary', 'price', - 'category', + 'category_id', 'formats', 'commentable', 'tags', @@ -86,6 +87,11 @@ public function registerMediaCollections(): void $this->addMediaCollection('extract')->singleFile(); } + public function category() + { + return $this->belongsTo(Category::class); + } + public function publisher() { return $this->belongsTo(Publisher::class); diff --git a/examples/demo-laravel/app/Category.php b/examples/demo-laravel/app/Category.php new file mode 100644 index 00000000..ecbed9c0 --- /dev/null +++ b/examples/demo-laravel/app/Category.php @@ -0,0 +1,69 @@ +header('locale') ?: app()->getLocale(); + } + + public function parent() + { + return $this->belongsTo(Category::class); + } + + public function children() + { + return $this->hasMany(Category::class, 'parent_id'); + } + + public function buildSortQuery() + { + return static::query() + ->where('type', $this->type) + ->where('parent_id', $this->parent_id); + } + + public function toArray() + { + return [ + 'id' => $this->id, + 'name' => $this->name, + ]; + } + + public function __toString() + { + return $this->name; + } +} diff --git a/examples/demo-laravel/app/Http/Controllers/BookController.php b/examples/demo-laravel/app/Http/Controllers/BookController.php index fbeab5fd..874e9eb9 100644 --- a/examples/demo-laravel/app/Http/Controllers/BookController.php +++ b/examples/demo-laravel/app/Http/Controllers/BookController.php @@ -49,7 +49,7 @@ public function index() AllowedFilter::scope('published_after'), ]) ->allowedSorts(['id', 'isbn', 'title', 'price', 'publication_date']) - ->allowedIncludes(['publisher', 'authors', 'reviews', 'media']) + ->allowedIncludes(['category', 'publisher', 'authors', 'reviews', 'media']) ->paginate() ); } @@ -62,7 +62,7 @@ public function index() */ public function show(Book $book) { - return new BookResource($book->load(['publisher', 'authors', 'reviews', 'media'])); + return new BookResource($book->load(['category', 'publisher', 'authors', 'reviews', 'media'])); } /** diff --git a/examples/demo-laravel/app/Http/Controllers/CategoryController.php b/examples/demo-laravel/app/Http/Controllers/CategoryController.php new file mode 100644 index 00000000..eb3332e7 --- /dev/null +++ b/examples/demo-laravel/app/Http/Controllers/CategoryController.php @@ -0,0 +1,91 @@ +authorizeResource(Category::class); + } + + /** + * Display a listing of the resource. + * + * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection + */ + public function index() + { + return CategoryResource::collection( + QueryBuilder::for(Category::class) + ->allowedFields(['id', 'name']) + ->allowedFilters([ + AllowedFilter::exact('id'), + AllowedFilter::exact('type'), + 'name', + ]) + ->ordered() + ->get() + ); + } + + /** + * Display the specified resource. + * + * @param \App\Category $category + * @return CategoryResource + */ + public function show(Category $category) + { + return new CategoryResource($category); + } + + /** + * Store a newly created resource in storage. + * + * @param Request $request + * @return CategoryResource + */ + public function store(StoreCategory $request) + { + $category = Category::create($request->all()); + + return new CategoryResource($category); + } + + /** + * Update the specified resource in storage. + * + * @param UpdateCategory $request + * @param \App\Category $category + * @return CategoryResource + */ + public function update(UpdateCategory $request, Category $category) + { + $category->update($request->all()); + + return new CategoryResource($category); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Category $category + * @return \Illuminate\Http\Response + * @throws \Exception + */ + public function destroy(Category $category) + { + $category->delete(); + + return response()->noContent(); + } +} diff --git a/examples/demo-laravel/app/Http/Requests/StoreBook.php b/examples/demo-laravel/app/Http/Requests/StoreBook.php index d74ab36c..a4e3a6b4 100644 --- a/examples/demo-laravel/app/Http/Requests/StoreBook.php +++ b/examples/demo-laravel/app/Http/Requests/StoreBook.php @@ -31,7 +31,6 @@ public function rules() 'publisher_id' => 'required', 'isbn' => 'required|isbn', 'title' => 'required', - 'category' => 'required|in:novel,comic,cook,economy,politics,history,fantasy,biography', 'description' => 'required', 'formats.*' => 'in:pocket,paperback,pdf,epub,kindle', 'price' => 'required|numeric', diff --git a/examples/demo-laravel/app/Http/Requests/StoreCategory.php b/examples/demo-laravel/app/Http/Requests/StoreCategory.php new file mode 100644 index 00000000..5df76788 --- /dev/null +++ b/examples/demo-laravel/app/Http/Requests/StoreCategory.php @@ -0,0 +1,31 @@ + 'required', + 'type' => 'required|in:book', + ]; + } +} diff --git a/examples/demo-laravel/app/Http/Requests/UpdateBook.php b/examples/demo-laravel/app/Http/Requests/UpdateBook.php index 24cc4317..e04d57e7 100644 --- a/examples/demo-laravel/app/Http/Requests/UpdateBook.php +++ b/examples/demo-laravel/app/Http/Requests/UpdateBook.php @@ -31,7 +31,6 @@ public function rules() 'publisher_id' => 'sometimes|required', 'isbn' => 'sometimes|required|isbn', 'title' => 'sometimes|required', - 'category' => 'sometimes|required|in:novel,comic,cook,economy,politics,history,fantasy,biography', 'description' => 'sometimes|required', 'formats.*' => 'sometimes|in:pocket,paperback,pdf,epub,kindle', 'price' => 'sometimes|required|numeric', diff --git a/examples/demo-laravel/app/Http/Requests/UpdateCategory.php b/examples/demo-laravel/app/Http/Requests/UpdateCategory.php new file mode 100644 index 00000000..c5b1603f --- /dev/null +++ b/examples/demo-laravel/app/Http/Requests/UpdateCategory.php @@ -0,0 +1,31 @@ + 'sometimes|required', + 'type' => 'sometimes|required|in:book', + ]; + } +} diff --git a/examples/demo-laravel/app/Http/Resources/Book.php b/examples/demo-laravel/app/Http/Resources/Book.php index 4a41f707..85e89bcc 100644 --- a/examples/demo-laravel/app/Http/Resources/Book.php +++ b/examples/demo-laravel/app/Http/Resources/Book.php @@ -16,6 +16,7 @@ public function toArray($request) { $attributes = parent::toArray($request); + $attributes['category'] = Category::make($this->whenLoaded('category')); $attributes['publisher'] = Publisher::make($this->whenLoaded('publisher')); $attributes['authors'] = Author::collection($this->whenLoaded('authors')); $attributes['reviews'] = Review::collection($this->whenLoaded('reviews')); diff --git a/examples/demo-laravel/app/Http/Resources/Category.php b/examples/demo-laravel/app/Http/Resources/Category.php new file mode 100644 index 00000000..a96c9b03 --- /dev/null +++ b/examples/demo-laravel/app/Http/Resources/Category.php @@ -0,0 +1,19 @@ +hasRole('admin')) { + return false; + } + } + + /** + * Determine whether the user can view any categories. + * + * @param \App\User $user + * @return mixed + */ + public function viewAny(User $user) + { + return true; + } + + /** + * Determine whether the user can view the category. + * + * @param \App\User $user + * @param \App\Category $category + * @return mixed + */ + public function view(User $user, Category $category) + { + return true; + } + + /** + * Determine whether the user can create categories. + * + * @param \App\User $user + * @return mixed + */ + public function create(User $user) + { + return true; + } + + /** + * Determine whether the user can update the category. + * + * @param \App\User $user + * @param \App\Category $category + * @return mixed + */ + public function update(User $user, Category $category) + { + return true; + } + + /** + * Determine whether the user can delete the category. + * + * @param \App\User $user + * @param \App\Category $category + * @return mixed + */ + public function delete(User $user, Category $category) + { + return false; + } +} diff --git a/examples/demo-laravel/app/User.php b/examples/demo-laravel/app/User.php index dc4ea6d3..69583034 100644 --- a/examples/demo-laravel/app/User.php +++ b/examples/demo-laravel/app/User.php @@ -22,15 +22,16 @@ * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications * @property-read int|null $notifications_count - * @method static \Illuminate\Database\Eloquent\Builder|\App\User newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|\App\User newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|\App\User query() - * @mixin \Eloquent * @property-read \Illuminate\Database\Eloquent\Collection|\App\Author[] $authors * @property-read int|null $authors_count * @property-read \Illuminate\Database\Eloquent\Collection|\App\Publisher[] $publishers * @property-read int|null $publishers_count + * @property-read bool $impersonate * @method static \Illuminate\Database\Eloquent\Builder|\App\User role($role) + * @method static \Illuminate\Database\Eloquent\Builder|\App\User newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\User newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|\App\User query() + * @mixin \Eloquent */ class User extends Authenticatable implements JWTSubject { diff --git a/examples/demo-laravel/composer.json b/examples/demo-laravel/composer.json index 977c317f..d9993fa0 100644 --- a/examples/demo-laravel/composer.json +++ b/examples/demo-laravel/composer.json @@ -18,6 +18,7 @@ "laravel/sanctum": "^2.0", "laravel/tinker": "^2.1", "laravel/ui": "^2.0", + "spatie/eloquent-sortable": "^3.8", "symfony/yaml": "^5.0", "tymon/jwt-auth": "^1.0", "vtec/laravel-crud": "dev-master" diff --git a/examples/demo-laravel/composer.lock b/examples/demo-laravel/composer.lock index 3040166d..14c80fd2 100644 --- a/examples/demo-laravel/composer.lock +++ b/examples/demo-laravel/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8bacc94194282843f9b389635834513b", + "content-hash": "6716afc0f8fc9b761eb3f8b570ee7eb3", "packages": [ { "name": "asm89/stack-cors", @@ -479,16 +479,16 @@ }, { "name": "egulias/email-validator", - "version": "2.1.19", + "version": "2.1.20", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "840d5603eb84cc81a6a0382adac3293e57c1c64c" + "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/840d5603eb84cc81a6a0382adac3293e57c1c64c", - "reference": "840d5603eb84cc81a6a0382adac3293e57c1c64c", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/f46887bc48db66c7f38f668eb7d6ae54583617ff", + "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff", "shasum": "" }, "require": { @@ -533,7 +533,7 @@ "validation", "validator" ], - "time": "2020-08-08T21:28:19+00:00" + "time": "2020-09-06T13:44:32+00:00" }, { "name": "fideloper/proxy", @@ -808,16 +808,16 @@ }, { "name": "intervention/validation", - "version": "2.4.1", + "version": "2.4.2", "source": { "type": "git", "url": "https://github.com/Intervention/validation.git", - "reference": "1637e3c6a0f32fd3fcefaff56eff09831cd32bb5" + "reference": "f49e6d621edd801bcaf1a40899a5529f9f938823" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/validation/zipball/1637e3c6a0f32fd3fcefaff56eff09831cd32bb5", - "reference": "1637e3c6a0f32fd3fcefaff56eff09831cd32bb5", + "url": "https://api.github.com/repos/Intervention/validation/zipball/f49e6d621edd801bcaf1a40899a5529f9f938823", + "reference": "f49e6d621edd801bcaf1a40899a5529f9f938823", "shasum": "" }, "require": { @@ -865,7 +865,7 @@ "validation", "validator" ], - "time": "2020-06-05T07:55:39+00:00" + "time": "2020-09-04T16:52:53+00:00" }, { "name": "itsgoingd/clockwork", @@ -1942,16 +1942,16 @@ }, { "name": "nesbot/carbon", - "version": "2.39.0", + "version": "2.39.1", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "0a41ea7f7fedacf307b7a339800e10356a042918" + "reference": "7af467873250583cc967a59ee9df29fabab193c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/0a41ea7f7fedacf307b7a339800e10356a042918", - "reference": "0a41ea7f7fedacf307b7a339800e10356a042918", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7af467873250583cc967a59ee9df29fabab193c1", + "reference": "7af467873250583cc967a59ee9df29fabab193c1", "shasum": "" }, "require": { @@ -1964,7 +1964,7 @@ "doctrine/orm": "^2.7", "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", "kylekatarnls/multi-tester": "^2.0", - "phpmd/phpmd": "^2.8", + "phpmd/phpmd": "^2.9", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^0.12.35", "phpunit/phpunit": "^7.5 || ^8.0", @@ -2027,7 +2027,7 @@ "type": "tidelift" } ], - "time": "2020-08-24T12:35:58+00:00" + "time": "2020-09-04T13:11:37+00:00" }, { "name": "nikic/php-parser", @@ -2083,16 +2083,16 @@ }, { "name": "opis/closure", - "version": "3.5.6", + "version": "3.5.7", "source": { "type": "git", "url": "https://github.com/opis/closure.git", - "reference": "e8d34df855b0a0549a300cb8cb4db472556e8aa9" + "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/e8d34df855b0a0549a300cb8cb4db472556e8aa9", - "reference": "e8d34df855b0a0549a300cb8cb4db472556e8aa9", + "url": "https://api.github.com/repos/opis/closure/zipball/4531e53afe2fc660403e76fb7644e95998bff7bf", + "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf", "shasum": "" }, "require": { @@ -2140,7 +2140,7 @@ "serialization", "serialize" ], - "time": "2020-08-11T08:46:50+00:00" + "time": "2020-09-06T17:02:15+00:00" }, { "name": "org_heigl/ghostscript", @@ -2844,6 +2844,63 @@ ], "time": "2020-08-18T17:17:46+00:00" }, + { + "name": "spatie/eloquent-sortable", + "version": "3.8.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/eloquent-sortable.git", + "reference": "18451ecf44b28a14ac0ed8803d0f5195a78a48a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/eloquent-sortable/zipball/18451ecf44b28a14ac0ed8803d0f5195a78a48a2", + "reference": "18451ecf44b28a14ac0ed8803d0f5195a78a48a2", + "shasum": "" + }, + "require": { + "illuminate/database": "~5.8.0|^6.0|^7.0", + "illuminate/support": "~5.8.0|^6.0|^7.0", + "php": "^7.2" + }, + "require-dev": { + "orchestra/testbench": "~3.8.0|^4.0|^5.0", + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\EloquentSortable\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be" + } + ], + "description": "Sortable behaviour for eloquent models", + "homepage": "https://github.com/spatie/eloquent-sortable", + "keywords": [ + "behaviour", + "eloquent", + "laravel", + "model", + "sort", + "sortable" + ], + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2020-07-08T00:05:43+00:00" + }, { "name": "spatie/image", "version": "1.7.6", @@ -3106,26 +3163,26 @@ }, { "name": "spatie/laravel-translatable", - "version": "4.4.0", + "version": "4.4.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-translatable.git", - "reference": "7642b38df16a61e5738ed00c912ca522bb32b554" + "reference": "cc2ce05f2dc9c7eca1ca79613d7dd3026c31e418" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-translatable/zipball/7642b38df16a61e5738ed00c912ca522bb32b554", - "reference": "7642b38df16a61e5738ed00c912ca522bb32b554", + "url": "https://api.github.com/repos/spatie/laravel-translatable/zipball/cc2ce05f2dc9c7eca1ca79613d7dd3026c31e418", + "reference": "cc2ce05f2dc9c7eca1ca79613d7dd3026c31e418", "shasum": "" }, "require": { - "illuminate/database": "~5.8.0|^6.0|^7.0", - "illuminate/support": "~5.8.0|^6.0|^7.0", + "illuminate/database": "~5.8.0|^6.0|^7.0|^8.0", + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0", "php": "^7.2" }, "require-dev": { "mockery/mockery": "^1.3", - "orchestra/testbench": "~3.8.0|^4.0|^5.0" + "orchestra/testbench": "~3.8.0|^4.0|^5.0|^6.0" }, "type": "library", "extra": { @@ -3180,7 +3237,7 @@ "type": "custom" } ], - "time": "2020-07-09T07:05:40+00:00" + "time": "2020-09-06T19:02:11+00:00" }, { "name": "spatie/pdf-to-image", @@ -8221,16 +8278,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.1.6", + "version": "9.1.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d25b24b1cd14772bde4d75daeb393dc17db9f6e6" + "reference": "2ef92bec3186a827faf7362ff92ae4e8ec2e49d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d25b24b1cd14772bde4d75daeb393dc17db9f6e6", - "reference": "d25b24b1cd14772bde4d75daeb393dc17db9f6e6", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2ef92bec3186a827faf7362ff92ae4e8ec2e49d2", + "reference": "2ef92bec3186a827faf7362ff92ae4e8ec2e49d2", "shasum": "" }, "require": { @@ -8290,7 +8347,7 @@ "type": "github" } ], - "time": "2020-08-31T06:31:46+00:00" + "time": "2020-09-03T07:09:19+00:00" }, { "name": "phpunit/php-file-iterator", diff --git a/examples/demo-laravel/config/elfinder.php b/examples/demo-laravel/config/elfinder.php index 3dcdfd29..4941557b 100644 --- a/examples/demo-laravel/config/elfinder.php +++ b/examples/demo-laravel/config/elfinder.php @@ -10,7 +10,7 @@ | The dir where to store the images (relative from public) | */ - 'dir' => ['storage/files'], + 'dir' => [], /* |-------------------------------------------------------------------------- @@ -26,7 +26,9 @@ | ] */ 'disks' => [ - + 'files' => [ + 'alias' => 'Local storage', + ], ], /* diff --git a/examples/demo-laravel/config/filesystems.php b/examples/demo-laravel/config/filesystems.php index c4c20371..d3ee6aab 100644 --- a/examples/demo-laravel/config/filesystems.php +++ b/examples/demo-laravel/config/filesystems.php @@ -62,6 +62,13 @@ 'visibility' => 'public', ], + 'files' => [ + 'driver' => 'local', + 'root' => storage_path('app/public/files'), + 'url' => env('APP_URL').'/storage/files', + 'visibility' => 'public', + ], + 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), diff --git a/examples/demo-laravel/database/factories/BookFactory.php b/examples/demo-laravel/database/factories/BookFactory.php index 8a95ee62..bf1ab536 100644 --- a/examples/demo-laravel/database/factories/BookFactory.php +++ b/examples/demo-laravel/database/factories/BookFactory.php @@ -15,7 +15,6 @@ 'en' => ucfirst($faker->words($faker->numberBetween(1, 5), true)), 'fr' => ucfirst($faker->words($faker->numberBetween(1, 5), true)), ], - 'category' => $faker->randomElement(['novel', 'comic', 'cook', 'economy', 'politics', 'history', 'fantasy', 'biography']), 'description' => [ 'en' => $faker->paragraph(10), 'fr' => $faker->paragraph(10), @@ -30,7 +29,7 @@ ), 'price' => $faker->randomFloat(2, 10, 50), 'commentable' => $faker->boolean(80), - 'tags' => $faker->unique()->words($faker->numberBetween(2, 5)), + 'tags' => collect($faker->words($faker->numberBetween(2, 5)))->unique(), 'publication_date' => $faker->dateTime, ]; }); diff --git a/examples/demo-laravel/database/migrations/2020_02_01_093550_create_categories_table.php b/examples/demo-laravel/database/migrations/2020_02_01_093550_create_categories_table.php new file mode 100644 index 00000000..3085e5df --- /dev/null +++ b/examples/demo-laravel/database/migrations/2020_02_01_093550_create_categories_table.php @@ -0,0 +1,37 @@ +id(); + $table->json('name'); + $table->string('type'); + $table->unsignedBigInteger('parent_id')->nullable(); + $table->unsignedInteger('order_column')->nullable(); + $table->timestamps(); + + $table->foreign('parent_id')->references('id')->on('categories')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('categories'); + } +} diff --git a/examples/demo-laravel/database/migrations/2020_02_26_153609_add_books_category_column.php b/examples/demo-laravel/database/migrations/2020_02_26_153609_add_books_category_column.php index 3fa3f612..e3becafe 100644 --- a/examples/demo-laravel/database/migrations/2020_02_26_153609_add_books_category_column.php +++ b/examples/demo-laravel/database/migrations/2020_02_26_153609_add_books_category_column.php @@ -14,7 +14,9 @@ class AddBooksCategoryColumn extends Migration public function up() { Schema::table('books', function (Blueprint $table) { - $table->string('category')->after('title'); + $table->unsignedBigInteger('category_id')->after('title')->nullable(); + + $table->foreign('category_id')->references('id')->on('categories')->nullOnDelete(); }); } diff --git a/examples/demo-laravel/database/seeds/BookSeeder.php b/examples/demo-laravel/database/seeds/BookSeeder.php index 19eda041..f88728ce 100644 --- a/examples/demo-laravel/database/seeds/BookSeeder.php +++ b/examples/demo-laravel/database/seeds/BookSeeder.php @@ -1,5 +1,8 @@ make()->each(function (\App\Book $book) use ($publishers, $authors, $faker) { + factory(Book::class, 500)->make()->each(function (Book $book) use ($categories, $publishers, $authors, $faker) { + $book->category()->associate($categories->random()); $book->publisher()->associate($publishers->random()); $book->save(); diff --git a/examples/demo-laravel/database/seeds/CategorySeeder.php b/examples/demo-laravel/database/seeds/CategorySeeder.php new file mode 100644 index 00000000..6d061afe --- /dev/null +++ b/examples/demo-laravel/database/seeds/CategorySeeder.php @@ -0,0 +1,258 @@ + [ + 'en' => 'Comics', + 'fr' => 'BD', + ], + 'children' => [ + [ + 'name' => [ + 'en' => 'Marvel', + 'fr' => 'Marvel', + ], + ], + [ + 'name' => [ + 'en' => 'Detective Comics', + 'fr' => 'Detective Comics', + ], + ], + [ + 'name' => [ + 'en' => 'Manga', + 'fr' => 'Manga', + ], + ], + ], + ], + [ + 'name' => [ + 'en' => 'Novel', + 'fr' => 'Roman', + ], + 'children' => [ + [ + 'name' => [ + 'en' => 'Thriller', + 'fr' => 'Thriller', + ], + ], + [ + 'name' => [ + 'en' => 'Adventure', + 'fr' => 'Aventure', + ], + ], + [ + 'name' => [ + 'en' => 'Fantasy', + 'fr' => 'Fantastique', + ], + ], + [ + 'name' => [ + 'en' => 'SF', + 'fr' => 'SF', + ], + ], + ], + ], + [ + 'name' => [ + 'en' => 'Culture', + 'fr' => 'Culture', + ], + 'children' => [ + [ + 'name' => [ + 'en' => 'History', + 'fr' => 'Histoire', + ], + ], + [ + 'name' => [ + 'en' => 'Biography', + 'fr' => 'Biographie', + ], + ], + [ + 'name' => [ + 'en' => 'Cinema', + 'fr' => 'Cinéma', + ], + ], + [ + 'name' => [ + 'en' => 'Music', + 'fr' => 'Musique', + ], + ], + [ + 'name' => [ + 'en' => 'Politics', + 'fr' => 'Politique', + ], + ], + [ + 'name' => [ + 'en' => 'Economy', + 'fr' => 'Economie', + ], + ], + ], + ], + [ + 'name' => [ + 'en' => 'Life', + 'fr' => 'Vie', + ], + 'children' => [ + [ + 'name' => [ + 'en' => 'Cook', + 'fr' => 'Cuisine', + ], + ], + [ + 'name' => [ + 'en' => 'Eroticism', + 'fr' => 'Eroticisme', + ], + ], + [ + 'name' => [ + 'en' => 'Health', + 'fr' => 'Santé', + ], + ], + [ + 'name' => [ + 'en' => 'DIY', + 'fr' => 'Bricolage', + ], + ], + [ + 'name' => [ + 'en' => 'Sports', + 'fr' => 'Sports', + ], + ], + [ + 'name' => [ + 'en' => 'Travel', + 'fr' => 'Voyage', + ], + ], + [ + 'name' => [ + 'en' => 'Animals', + 'fr' => 'Animaux', + ], + ], + ], + ], + [ + 'name' => [ + 'en' => 'Knowledge', + 'fr' => 'Savoir', + ], + 'children' => [ + [ + 'name' => [ + 'en' => 'School', + 'fr' => 'Ecole', + ], + ], + [ + 'name' => [ + 'en' => 'Dictionary', + 'fr' => 'Dictionnaires', + ], + ], + [ + 'name' => [ + 'en' => 'Languages', + 'fr' => 'Langues', + ], + ], + [ + 'name' => [ + 'en' => 'Law', + 'fr' => 'Droit', + ], + ], + [ + 'name' => [ + 'en' => 'Science', + 'fr' => 'Science', + ], + ], + [ + 'name' => [ + 'en' => 'Computer science', + 'fr' => 'Informatique', + ], + 'children' => [ + [ + 'name' => [ + 'en' => 'Hardware', + 'fr' => 'Matériel', + ], + ], + [ + 'name' => [ + 'en' => 'Software', + 'fr' => 'Logiciel', + ], + ], + ], + ], + [ + 'name' => [ + 'en' => 'Business', + 'fr' => 'Entreprise', + ], + ], + ], + ], + ]; + + foreach ($categories as $category) { + $parent = Category::create([ + 'type' => 'book', + 'name' => $category['name'], + ]); + + if (! empty($category['children'])) { + foreach ($category['children'] as $child) { + $subParent = $parent->children()->create([ + 'type' => 'book', + 'name' => $child['name'], + ]); + + if (! empty($child['children'])) { + $subParent->children()->createMany( + collect($child['children']) + ->map(function ($item) { + return [ + 'type' => 'book', + 'name' => $item['name'], + ]; + }) + ->toArray() + ); + } + } + } + } + } +} diff --git a/examples/demo-laravel/database/seeds/DatabaseSeeder.php b/examples/demo-laravel/database/seeds/DatabaseSeeder.php index 5306dd2c..f9ca9071 100644 --- a/examples/demo-laravel/database/seeds/DatabaseSeeder.php +++ b/examples/demo-laravel/database/seeds/DatabaseSeeder.php @@ -12,16 +12,12 @@ class DatabaseSeeder extends Seeder */ public function run() { - /** - * Cleanup media - */ - foreach (Storage::disk('public')->allDirectories('media') as $dir) { - Storage::disk('public')->deleteDirectory($dir); - } + Storage::disk('public')->deleteDirectory('media'); $this->call(UserSeeder::class); $this->call(PublisherSeeder::class); $this->call(AuthorSeeder::class); + $this->call(CategorySeeder::class); $this->call(BookSeeder::class); $this->call(ReviewSeeder::class); } diff --git a/examples/demo-laravel/routes/api.php b/examples/demo-laravel/routes/api.php index 194724c1..56bbb2e6 100644 --- a/examples/demo-laravel/routes/api.php +++ b/examples/demo-laravel/routes/api.php @@ -34,5 +34,6 @@ 'books' => 'BookController', 'reviews' => 'ReviewController', 'publishers' => 'PublisherController', + 'categories' => 'CategoryController', ]); }); diff --git a/examples/demo-laravel/storage/app/public/.gitignore b/examples/demo-laravel/storage/app/public/.gitignore index 040a7727..d6b7ef32 100644 --- a/examples/demo-laravel/storage/app/public/.gitignore +++ b/examples/demo-laravel/storage/app/public/.gitignore @@ -1,4 +1,2 @@ * -!files -!media !.gitignore diff --git a/examples/demo-laravel/storage/app/public/files/.gitignore b/examples/demo-laravel/storage/app/public/files/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/examples/demo-laravel/storage/app/public/files/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/examples/demo-laravel/storage/app/public/media/.gitignore b/examples/demo-laravel/storage/app/public/media/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/examples/demo-laravel/storage/app/public/media/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/examples/demo/src/locales/en.json b/examples/demo/src/locales/en.json index b745829c..ef751348 100644 --- a/examples/demo/src/locales/en.json +++ b/examples/demo/src/locales/en.json @@ -77,16 +77,6 @@ }, "books": { "enums": { - "category": { - "novel": "Novel", - "comic": "Comic", - "cook": "Cook", - "economy": "Economy", - "politics": "Politics", - "history": "History", - "fantasy": "Fantasy", - "biography": "Biography" - }, "formats": { "pocket": "Pocket", "paperback": "Paperback", diff --git a/examples/demo/src/locales/fr.json b/examples/demo/src/locales/fr.json index f3e43a34..f2afb8e9 100644 --- a/examples/demo/src/locales/fr.json +++ b/examples/demo/src/locales/fr.json @@ -156,16 +156,6 @@ "cheaper_than": "Moins cher que" }, "enums": { - "category": { - "novel": "Roman", - "comic": "BD", - "cook": "Cuisine", - "economy": "Economie", - "politics": "Politique", - "history": "Histoire", - "fantasy": "Fantastique", - "biography": "Biographie" - }, "formats": { "pocket": "Poche", "paperback": "Broché", diff --git a/examples/demo/src/resources/books/Form.vue b/examples/demo/src/resources/books/Form.vue index c13d46f5..161d374f 100644 --- a/examples/demo/src/resources/books/Form.vue +++ b/examples/demo/src/resources/books/Form.vue @@ -17,7 +17,12 @@ reference="publishers" > - + - + + + diff --git a/packages/admin/src/components/ui/providers/List.vue b/packages/admin/src/components/ui/providers/List.vue index ec48e87a..f1261958 100644 --- a/packages/admin/src/components/ui/providers/List.vue +++ b/packages/admin/src/components/ui/providers/List.vue @@ -316,7 +316,8 @@ export default { ...f, type: f.type || "text", label: - f.label || this.$admin.getSourceLabel(this.resource, f.source), + f.label || + this.$admin.getSourceLabel(this.resource, f.labelKey || f.source), }; }); }, diff --git a/packages/admin/src/components/ui/wrappers/Field.vue b/packages/admin/src/components/ui/wrappers/Field.vue index e029c8f4..f997d42a 100644 --- a/packages/admin/src/components/ui/wrappers/Field.vue +++ b/packages/admin/src/components/ui/wrappers/Field.vue @@ -1,5 +1,5 @@