From 7b1914bfe815836a0d73da2f61699bae2a0617ad Mon Sep 17 00:00:00 2001 From: Chri$ Date: Sat, 31 Aug 2024 13:07:45 +0200 Subject: [PATCH 1/3] test: Add features test for category (Can create , Can create with parent...) --- .../database/factories/CategoryFactory.php | 2 +- tests/src/Admin/Features/CategoryTest.php | 90 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/src/Admin/Features/CategoryTest.php diff --git a/packages/core/database/factories/CategoryFactory.php b/packages/core/database/factories/CategoryFactory.php index 9f320d82c..5d3ae456c 100755 --- a/packages/core/database/factories/CategoryFactory.php +++ b/packages/core/database/factories/CategoryFactory.php @@ -26,7 +26,7 @@ public function definition(): array 'name' => $name = $this->faker->unique()->words(3, true), 'slug' => Str::slug($name), 'description' => $this->faker->realText(), - 'is_visible' => $this->faker->boolean(), + 'is_enabled' => $this->faker->boolean(), 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'), 'updated_at' => $this->faker->dateTimeBetween('-5 month'), ]; diff --git a/tests/src/Admin/Features/CategoryTest.php b/tests/src/Admin/Features/CategoryTest.php new file mode 100644 index 000000000..1ac49ea79 --- /dev/null +++ b/tests/src/Admin/Features/CategoryTest.php @@ -0,0 +1,90 @@ +assertFound(); + + livewire(Pages\Category\Index::class) + ->assertSee(__('shopper::pages/categories.menu')); + }); + + it('can validate `required` fields on add categorie form', function (): void { + livewire(CategoryForm::class) + ->assertFormExists() + ->fillForm([]) + ->call('save') + ->assertHasFormErrors(['name' => 'required']); + }); + + it('can create a categorie', function (): void { + livewire(CategoryForm::class) + ->assertFormExists() + ->fillForm([ + 'name' => 'My new Category', + ]) + ->call('save') + ->assertHasNoFormErrors() + ->assertDispatched('category-save'); + + expect((new CategoryRepository)->count())->toBe(1); + }); + + it('will generate a slug when brand slug already exists', function (): void { + Category::factory()->create(['name' => 'Old category', 'slug' => 'my-first-category']); + + livewire(CategoryForm::class) + ->assertFormExists() + ->fillForm([ + 'name' => 'My first category', + ]) + ->call('save') + ->assertDispatched('category-save'); + + expect((new CategoryRepository)->count()) + ->toBe(2) + ->and((new CategoryRepository)->getById(2)?->slug) + ->toBe('my-first-category-1'); + }); + + it('can create category with parent', function (): void { + $parent = Category::factory()->create(['name' => 'Parent']); + + livewire(CategoryForm::class) + ->assertFormExists() + ->fillForm([ + 'name' => 'My new Category', + 'parent_id' => $parent->id, + ]) + ->call('save') + ->assertHasNoFormErrors() + ->assertDispatched('category-save'); + expect((new CategoryRepository)->count())->toBe(2); + }); + + it('can set parent_id child to null if parent are deleted', function (): void { + $parent = Category::factory()->create(['name' => 'Parent']); + $child = Category::factory()->create(['name' => 'Enfant', 'parent_id' => $parent->id]); + + expect($child->parent_id)->toBe($parent->id); + + $parent->delete(); + + $child->refresh(); + expect($child->parent_id)->toBeNull(); + }); + +})->group('category'); From 2de9d966b446d6f4638a92c11c23037d238ccf4d Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Fri, 6 Sep 2024 14:13:44 +0200 Subject: [PATCH 2/3] refactor: update category test wording --- tests/src/Admin/Features/CategoryTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/src/Admin/Features/CategoryTest.php b/tests/src/Admin/Features/CategoryTest.php index 1ac49ea79..99dfad7c3 100644 --- a/tests/src/Admin/Features/CategoryTest.php +++ b/tests/src/Admin/Features/CategoryTest.php @@ -72,19 +72,19 @@ ->call('save') ->assertHasNoFormErrors() ->assertDispatched('category-save'); + expect((new CategoryRepository)->count())->toBe(2); }); - it('can set parent_id child to null if parent are deleted', function (): void { + it('has parent_id field null when parent category is deleted', function (): void { $parent = Category::factory()->create(['name' => 'Parent']); - $child = Category::factory()->create(['name' => 'Enfant', 'parent_id' => $parent->id]); + $child = Category::factory()->create(['name' => 'Child', 'parent_id' => $parent->id]); expect($child->parent_id)->toBe($parent->id); $parent->delete(); - $child->refresh(); + expect($child->parent_id)->toBeNull(); }); - })->group('category'); From dce96403781834a8f6aa237c817f42bd98a5e505 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Fri, 6 Sep 2024 15:59:33 +0200 Subject: [PATCH 3/3] docs: update category doc --- packages/admin/docs/content/brands.md | 15 ++-- packages/admin/docs/content/categories.md | 95 ++++++++++------------ packages/admin/docs/content/collections.md | 45 +++++++--- 3 files changed, 87 insertions(+), 68 deletions(-) diff --git a/packages/admin/docs/content/brands.md b/packages/admin/docs/content/brands.md index 3df6a1e6a..6214d6e14 100644 --- a/packages/admin/docs/content/brands.md +++ b/packages/admin/docs/content/brands.md @@ -6,6 +6,7 @@ Unless you make your own products, you'll be registering your product's brands i If you sell your own products, you must at least create your company as a brand: this helps your customer find what they are looking for, and this can bring some valuable search engine points. ## Overview + The management of brands is exactly the same as the one done in most of the e-commerce website creation tools: only the name can change. It is mainly used to facilitate the navigation of customers in your catalog, as it is increasingly common to search for a specific brand. @@ -17,7 +18,8 @@ It is mainly used to facilitate the navigation of customers in your catalog, as New brands are automatically activated and available for your online store, even if they do not contain any products yet. You must deactivate them so that they do not appear online. ### Fields -The model used is `Shopper\Framework\Models\Shop\Product\Brand`. + +The model used is `Shopper\Models\Brand`. | Name | Type | Required | Notes | |-------------------|----------|----------|----------------------------------------------------------| @@ -93,6 +95,7 @@ return [ ``` ## Manage Brands + The brands are accessible via the Brands Menu on the left sidebar. You can update the livewire page component in the configuration file to use your own. @@ -105,16 +108,18 @@ php artisan make:shopper-page Brand This page will extend shopper's default layout, and you can render the view you want. ### Create brand -Click on the "Create" button on the brands page, which will display and slideover. + +Click on the "Create" button on the brands page, which will display the form.
- Create brand + Create brand
Create brand
Save your changes in order to be taken back to the brand's list. Required fields are marked with an **asterisk (*)** ## Retrieve Data + Once you have your brands you want to display them in your store, you can retrieve them this way in your controller ```php @@ -191,7 +196,7 @@ class AppServiceProvider extends ServiceProvider And in your front-end you can browse your brands to have a display like this
- Brands preview list + Brands list
Brands example list
@@ -200,7 +205,7 @@ And in your front-end you can browse your brands to have a display like this Sometimes in your store, you won't have a brand name for your products (it's rare but possible), especially if you make them yourself. In this case, you can hide brands on the sidebar and disabled all brand-related functionalities in your store. -To disable brand-related functionalities, open the `features.php` configuration file in the `config/shopper` folder and set the brand key to disable. +To disable brands-related functionalities, open the `features.php` configuration file in the `config/shopper` folder and set the brand key to disable. ```php use Shopper\Enum\FeatureState; diff --git a/packages/admin/docs/content/categories.md b/packages/admin/docs/content/categories.md index b1a420470..8fd10073f 100644 --- a/packages/admin/docs/content/categories.md +++ b/packages/admin/docs/content/categories.md @@ -1,10 +1,13 @@ # Categories + Categories are the primary way to group products with similar features. You can also add subcategories if desired. For example, if you sell clothing, you might have “t-shirts”, “hoodies” and “pants” as categories. ## Overview -Shopper gives you a possibility to categorize your products in a very flexible way, which is one of the most vital functionalities of the modern e-commerce systems. The categories system in Shopper use the [Laravel Adjacency List](https://github.com/staudenmeir/laravel-adjacency-list) package to create categories trees like this + +Shopper gives you a possibility to categorize your products in a very flexible way, which is one of the most vital functionalities of the modern e-commerce systems. +The categories system in Shopper use the [Laravel Adjacency List](https://github.com/staudenmeir/laravel-adjacency-list) package to create categories trees like this ```plain theme:github-light Category @@ -32,19 +35,20 @@ Category ### Fields + The model used is `Shopper\Core\Models\Category`. -| Name | Type | Required | Notes | -|-------------|-----------|------------|------------| -| `id` | autoinc | | auto | -| `name` | string | yes | | -| `slug` | string | yes | Unique, default value is generated using category name | -| `description` | longText | no | Nullable | -| `position` | string | no | Default `0` | -| `is_enabled` | boolean | no | Default `false` | -| `seo_title` | string | no | Nullable, for seo title max length is 60 | -| `seo_description` | string | no | Nullable, for seo description max length is 160 | -| `parent_id` | bigint | no | | +| Name | Type | Required | Notes | +|-------------------|----------|----------|--------------------------------------------------------| +| `id` | autoinc | | auto | +| `name` | string | yes | | +| `slug` | string | yes | Unique, default value is generated using category name | +| `description` | longText | no | Nullable | +| `position` | string | no | Default `0` | +| `is_enabled` | boolean | no | Default `false` | +| `seo_title` | string | no | Nullable, for seo title max length is 60 | +| `seo_description` | string | no | Nullable, for seo description max length is 160 | +| `parent_id` | bigint | no | | :::tip Models are customizable, and we recommend changing the **Category** model when you configure your store. @@ -84,37 +88,36 @@ return [ 3. Update `category` key for the model on the `shopper/models.php` config file to use our new model ```php - return [ - 'models' => [ - // ... - 'brand' => \App\Models\Brand::class, - - // ... - 'category' => \App\Models\Category::class, // [tl! focus] - ] - ]; + 'category' => Models\Category::class, // [tl! --] + 'category' => \App\Models\Category::class, // [tl! ++] ``` ### Components -Livewire components for managing categories are available in the component configuration file `config/shopper/components.php`. -```php -use Shopper\Http\Livewire; +By default, categories Livewire components are not published. To customize components, you must publish them. -return [ - 'livewire' => [ +```bash +php artisan shopper:component:publish category +``` - 'categories.browse' => Livewire\Categories\Browse::class, - 'categories.create' => Livewire\Categories\Create::class, - 'categories.edit' => Livewire\Categories\Edit::class, +```php +use Shopper\Livewire; - 'tables.categories-table' => Livewire\Tables\CategoriesTable::class, +return [ - ]; + 'pages' => [ + 'category-index' => Livewire\Pages\Category\Index::class, + ]; + + 'components' => [ + 'slide-overs.category-form' => Livewire\SlideOvers\CategoryForm::class, + 'slide-overs.re-order-categories' => Livewire\SlideOvers\ReOrderCategories::class, + ], ]; ``` ## Manage Categories + Categories are determinant of how people will navigate on your site and search for your products. You should focus on your category tree and how categories are organized even before you start creating product sheets. The categories are accessible via the **Categories** Menu on the left sidebar. The display page is rendered by the Livewire component `Shopper\Framework\Http\Livewire\Categories\Browse` and for the display of the categories table is the component `Shopper\Framework\Http\Livewire\Tables\CategoriesTable`. @@ -122,22 +125,16 @@ The categories are accessible via the **Categories** Menu on the left sidebar. T You can modify them in the component configuration file to use your own. ### Create category + Click on the "Create" button on the categories page, and a creation form appears.
- Create category form + Create category form
Create category
Save your changes in order to be taken back to the categories list. Required fields are marked with an **asterisk (*)** - -The SEO section allows you to define how your category information should be displayed in search engines. To modify the content you click on the button "Edit SEO preview". It uses the same blade component as the brands. - -
- Seo form -
Seo form preview
-
- +The SEO section allows you to define how your category information should be displayed in search engines. Once you have finished configuring your category, save it, and you are ready to fill it with products. If you use another interface (e.g. API) to save your categories, you can save directly using your Model @@ -153,7 +150,6 @@ $category = Category::create([ ``` The slug cannot be null, you have to fill in the value of the category name and according to that the slug will be generated. - In case a slug already exists, the slug will be automatically extended to prevent duplicates: ```php @@ -170,7 +166,6 @@ echo $category2->slug; ``` And if the category has a parent, the child's slug will be generated with the parent's directly - This generation is done when adding a category in Shopper. But you can change this behavior by extending the category create [Shopper\Framework\Http\Livewire\Categories\Create](https://github.com/shopperlabs/framework/blob/main/src/Http/Livewire/Categories/Create.php) Livewire component or by creating a new one. ```php @@ -178,8 +173,8 @@ use App\Models\Category; $category = Category::create(['name' => 'Photo', 'slug' => 'photo']); $categoryChild = Category::create([ - 'name' => 'Camera', - 'slug' => $this->parent ? $this->parent->slug . '-' . 'Camera' : 'Camera', + 'name' => $name = 'Camera', + 'slug' => $this->parent ? $this->parent->slug . '-' . $name : $name, 'parent_id' => $caregory->id ]); @@ -209,22 +204,16 @@ $child = Category::create([ ]); ``` -On Shopper, to specify the parent category you just have to choose via the select field - -
- category parent -
Category parent
-
- ### Update category -The "Modify" button allows you to modify the category. + +The "Update" button allows you to modify the category. :::info It is important to know that if you update the category name, the slug will automatically be updated as well. :::
- update category + update category
Update Category
diff --git a/packages/admin/docs/content/collections.md b/packages/admin/docs/content/collections.md index a62cadaf1..5d231ac11 100644 --- a/packages/admin/docs/content/collections.md +++ b/packages/admin/docs/content/collections.md @@ -1,4 +1,5 @@ # Collections + Collections, although not strictly the same, are akin to Categories. They serve to allow you to add products, either explicitly or via certain criteria, for use on your store. In most e-commerce tools, collections are considered as categories. And especially on Shopify, collections are a great feature for grouping products. @@ -6,6 +7,7 @@ In most e-commerce tools, collections are considered as categories. And especial And for the constitution of the collections we got closer to what Shopify offers in terms of configuration, and the management of collections in Shopper is inspired from Shopify. ## Context + For example if you have a store that sells various electronic products, you will probably have categories like "Phones", "Computers", "Cameras" etc. Each of these categories may have several products that can be listed. And to try to group the products in an even more detailed way you can create for example a collection called "Gaming Collection" and specify in this collection that any product with certain conditions can be included. @@ -15,17 +17,21 @@ And that Gaming Collection can have products that even come from various categor Let's use Netflix as an example. Categories are essentially genres: adventure, action, horror, romance, etc., while collections are similar to a TV series or movie sequels that are ultimately meant to be viewed together. ## Collections vs Categories + The question is essential, it is difficult to find this type of configuration on e-commerce tools because most of the time categories, collections or taxonomies are used to perform the same action **group products**. The advantage of having collections in addition to categories in Shopper is to differentiate or optimize the search for products by customers in your store. ### Depth + A collection can't have a child or a parent like a category. So all the collections are at the same hierachical level. ### Condition + Where products can be added to any category, collections cannot. Depending on the type of collection you want to create (Manual or Automatic) you will find yourself creating conditions or rules for the products that should be in that collection. ## Overview + As mentioned above, the collections in Shopper are inspired by [Shopify collections](https://help.shopify.com/en/manual/products/collections). So there are also 2 types of collections: "Manual" and "Automatic" and the configuration for each is different.
@@ -34,6 +40,7 @@ As mentioned above, the collections in Shopper are inspired by [Shopify collecti
### Fields + As the collections can be automatic, they are managed by 2 Models, the Collection model and the model for rules associated with automatic collections. Manual collections do not need to have rules. @@ -66,7 +73,7 @@ Manual collections do not need to have rules. :::tip Models are customizable, and we recommend changing the **Collection** model when you configure your store. -To change the model you need to look at the configuration file `config/shopper/shopper.php`. +To change the model you need to look at the configuration file `config/shopper/models.php`. ::: Let's keep in mind the modification that was made in the previous section regarding [Categories](/categories). @@ -98,14 +105,15 @@ return [ } ``` -3. Update `Collection` key for the model on the `models.php` config file to use our new model +3. Update `collection` key for the model on the `models.php` config file to use our new model ```php 'collection' => Models\Collection::class, // [tl! --] 'collection' => \App\Models\Collection::class, // [tl! ++] ``` ### Components -By default, collection Livewire components are not published. To customize components, you must publish them. + +By default, collections Livewire components are not published. To customize components, you must publish them. ```bash php artisan shopper:component:publish collection @@ -137,6 +145,7 @@ return [ ``` ## Manage Collections + Form your Shopper admin on the sidebar go to **Collections**. The display page is rendered by the Livewire component `Shopper\Livewire\Pages\Collection\Index::class`.
@@ -145,17 +154,11 @@ Form your Shopper admin on the sidebar go to **Collections**. The display page i
### Create collection -Click on the "Create" button on the collections page, which will display and slideover. - -
- Create collection form -
Create collection
-
+Click on the "Create" button on the collections page, which will display and slideover. Save your changes in order to be taken back to the collection edit page. Required fields are marked with an **asterisk (*)**. You can create two types of collections as we said: `Automatic` and `Manual` collection. - Only automatic collections have rules for automating them. When you choose to create an automatic collection the rules section will be available in the edit form.
@@ -166,6 +169,7 @@ Only automatic collections have rules for automating them. When you choose to cr After you create a collection, you can't change its type. ## Retrieve Data + After extending (or not) the Shopper Collection Model you can use your own Model to retrieve data from the database. Here a code example. ```php @@ -183,3 +187,24 @@ To view the image of a collection you can consult the [Media documentation](/med collections data
Example of collections
+ +## Disabled Collection + +Sometimes in your store, you won't have a brand name for your products (it's rare but possible), especially if you make them yourself. +In this case, you can hide brands on the sidebar and disabled all brand-related functionalities in your store. + +To disable collections-related functionalities, open the `features.php` configuration file in the `config/shopper` folder and set the brand key to disable. + +```php +use Shopper\Enum\FeatureState; + +return [ + 'attribute' => FeatureState::Enabled, + 'brand' => FeatureState::Enabled, + 'category' => FeatureState::Enabled, + 'collection' => FeatureState::Enabled, // [tl! --] + 'collection' => FeatureState::Disabled, // [tl! ++] + 'discount' => FeatureState::Enabled, + 'review' => FeatureState::Enabled, +]; +```