From 5e3cca8a6eecfa5c4b606a39d9b44b2a73db64fe Mon Sep 17 00:00:00 2001
From: Sam Poyigi <6567634+sampoyigi@users.noreply.github.com>
Date: Thu, 11 Apr 2024 19:11:32 +0100
Subject: [PATCH 01/14] Update docs to v4
Signed-off-by: Sam Poyigi <6567634+sampoyigi@users.noreply.github.com>
---
installation.md | 117 +++++++++++++------------
php-coding-guidelines.md | 182 ++++++++++++++++++++++++++-------------
upgrade-guide.md | 161 +++++++++++++++++++++-------------
3 files changed, 283 insertions(+), 177 deletions(-)
diff --git a/installation.md b/installation.md
index e61ed31..9b89450 100644
--- a/installation.md
+++ b/installation.md
@@ -4,38 +4,73 @@ section: "getting-started"
sortOrder: 10
---
-Before you proceed with installing TastyIgniter, you should check that your server meets the minimum system
-requirements.
+As of Version 4, TastyIgniter can be installed as a stand-alone application, or as a package inside an existing Laravel
+application.
-## Minimum Requirements
+## Stand-alone Installation
-These are the requirements for your web hosting to run TastyIgniter:
+### Requirements
+
+These are the requirements to run TastyIgniter:
- **Apache** (with mod_rewrite enabled) or **Nginx**
-- **MySQL 5.7+** or **MariaDB 10.0.5+** or **PostgreSQL 9.6**
-- **PHP 8.0+** with the following extensions: pdo_mysql, curl, openssl, dom, gd, mbstring, json, tokenizer, zip
+- **PHP 8.2+** with the following extensions: bcmath, pdo_mysql, ctype, curl, openssl, dom, gd, exif, mbstring, json,
+ tokenizer, zip, xml
+- **MySQL 5.7+** or **MariaDB 10.3+** or **PostgreSQL 10.0**
- **Composer 2.0** or **higher** (for installing dependencies)
-## Installing TastyIgniter
+### Installing TastyIgniter
-TastyIgniter manages its dependencies and extensions using composer. To install the platform, use the `create-project` command in the terminal to create a project. The command below creates a new project in the directory `mytasty`.
+TastyIgniter manages its dependencies and extensions using
+composer. To install the platform, use the `create-project` command in the terminal to create a project. The command
+below creates a new project in the directory `mytasty`.
```bash
composer create-project tastyigniter/tastyigniter mytasty
```
-After running the above command, run the installation command and follow the instructions to complete installation
+From here, you can move on to the [Setting up TastyIgniter](#setting-up-tastyigniter) step.
+
+## Package Installation
+
+### Requirements
+
+These are the requirements to run TastyIgniter:
+
+- **Laravel 10+**
+- **MySQL 5.7+** or **MariaDB 10.3+** or **PostgreSQL 10.0**
+- **Composer 2.0** or **higher** (for installing dependencies)
+
+### Installing TastyIgniter
+
+To install TastyIgniter as a package from your command line, run the following command in your Laravel project
+directory:
+
+```bash
+composer require tastyigniter/core
+```
+
+From here, you can move on to the [Setting up TastyIgniter](#setting-up-tastyigniter) step.
+
+## Setting up TastyIgniter
+
+TastyIgniter comes packaged with a command-line setup utility that will get you up and running in a few minutes. It will
+attempt to automatically configure TastyIgniter and create an admin user account.
+
+In the TastyIgniter installation's root directory, run the following command:
```bash
php artisan igniter:install
```
-The installation command will guide you through the process of setting up TastyIgniter for the first time. It will ask
+The setup command will guide you through the process of setting up TastyIgniter for the first time. It will ask
for the database configuration, application URL and administrator details.
-**Command-line unattended installation**
+> You can safely run the command multiple times if needed.
-Some installations require an unattended mode so that the application can easily be built into automated infrastructure
+**Command-line unattended setup**
+
+Some setup require an unattended mode so that the application can easily be built into automated infrastructure
pipelines and build tools, e.g. Docker.
To run this, its similar to the above command, just instead we provide all of the option values up-front within the
@@ -45,30 +80,10 @@ projects
```bash
php artisan igniter:install --no-interaction
```
-### Wizard Installation
-
-1. [Download](https://github.com/tastyigniter/setup/archive/master.zip) and unzip the TastyIgniter setup wizard into an
- empty directory on your server.
-2. Create a MySQL user database for TastyIgniter on your database server.
-3. Upload the TastyIgniter folders and files to your server. Normally the setup.php file will be at the web root
- directory.
-4. Grant write permissions on the setup directory, its subdirectories and files.
-4. Run the TastyIgniter setup script by accessing setup.php in your web browser. Example, http://example.com/setup.php
- or http://example.com/folder/setup.php
-5. Follow all onscreen instructions and make sure all installation requirements are checked.
-
-
## Post-installation steps
-For security reasons, if you used the [Quick Installation Setup Wizard](#quick-installation), you should delete the
-setup files. TastyIgniter will never automatically delete files from your system, so these files and directories should
-be deleted manually:
-
-```yaml
-setup/ <== Setup directory
-setup.php <== Setup script
-```
+There are some things you may need to set up after the installation is complete.
### Setting up the task scheduler
@@ -83,7 +98,8 @@ Be sure to replace `/path/to/artisan` with the absolute path to the artisan file
This Cron will call the command scheduler every minute. When executing the `schedule:run` command, TastyIgniter will
assess your scheduled tasks and run the tasks that are due.
-> Task Scheduling is how scheduling time-based tasks are managed in TastyIgniter. Several core features of TastyIgniter, such as checking for updates, use the scheduler.
+> Task Scheduling is how scheduling time-based tasks are managed in TastyIgniter. Several core features of TastyIgniter,
+> such as checking for updates, use the scheduler.
### Setting up the queue daemon
@@ -144,22 +160,22 @@ As an example, your site conf file should look something like:
```html
server {
- listen 80;
+listen 80;
- root /path/to/tastyigniter;
- index index.php;
+root /path/to/tastyigniter;
+index index.php;
- server_name mytastysite.com;
+server_name mytastysite.com;
- gzip on;
- gzip_proxied expired no-cache no-store private auth;
- gzip_types text/plain text/css application/x-javascript application/json application/javascript image/x-icon image/png image/gif image/jpeg image/svg+xml;
+gzip on;
+gzip_proxied expired no-cache no-store private auth;
+gzip_types text/plain text/css application/x-javascript application/json application/javascript image/x-icon image/png image/gif image/jpeg image/svg+xml;
- charset utf-8;
+charset utf-8;
- access_log off;
+access_log off;
- include /path/to/tastyigniter/.nginx.conf;
+include /path/to/tastyigniter/.nginx.conf;
}
```
@@ -187,19 +203,6 @@ token is used to check that the authenticated user is the one who actually makes
Although CSRF security is enabled by default, you can disable it in the `config/system.php` configuration file using
the `enableCsrfProtection` parameter.
-### Bleeding edge updates
-
-TastyIgniter core and some marketplace extensions will introduce changes in two stages to ensure overall stability and
-integrity of the codebase. This means that besides the regular stable versions, they do have a test version.
-
-Replace the default TastyIgniter requirements in your `composer.json` file with the following to receive updates from
-the develop branch directly.
-
-```json
-"tastyigniter/flame": "dev-develop as 1.0",
-"laravel/framework": "6.0.*@dev",
-```
-
## Getting Started
You can access the administrator panel from `/admin` with your username and password asked during the setup process.
diff --git a/php-coding-guidelines.md b/php-coding-guidelines.md
index 6c6b4fd..062d987 100644
--- a/php-coding-guidelines.md
+++ b/php-coding-guidelines.md
@@ -6,11 +6,14 @@ sortOrder: 360
**Consistency is key**
-Coding Standards are critical for achieving high code quality. We can produce a homogeneous code that is easy to read and maintain by using a consistent visual style, naming conventions, and other technical settings.
+Coding Standards are critical for achieving high code quality. We can produce a homogeneous code that is easy to read
+and maintain by using a consistent visual style, naming conventions, and other technical settings.
## General PHP Rules
-Code style must follow [PSR-1](http://www.php-fig.org/psr/psr-1/), [PSR-2](http://www.php-fig.org/psr/psr-2/) and [PSR-12](https://www.php-fig.org/psr/psr-12/). Generally speaking, everything string-like (except Model attributes) should use camelCase. Detailed examples on these are spread throughout the guide in their relevant sections.
+Code style must follow [PSR-1](http://www.php-fig.org/psr/psr-1/), [PSR-2](http://www.php-fig.org/psr/psr-2/)
+and [PSR-12](https://www.php-fig.org/psr/psr-12/). Generally speaking, everything string-like (except Model attributes)
+should use camelCase. Detailed examples on these are spread throughout the guide in their relevant sections.
#### Class defaults
@@ -32,35 +35,84 @@ public ?string $variable;
public string|null $variable;
```
+#### Void return types
+
+If a method returns nothing, it should be indicated with void. This makes it more clear to the users of your code what
+your intention was when writing it.
+
+```php
+// in a Laravel model
+public function scopeArchived(Builder $query): void
+{
+ $query->
+ ...
+}
+```
+
+#### Typed properties
+
+You should type a property whenever possible. Don't use a docblock.
+
+```php
+class Foo
+{
+ public string $bar;
+}
+```
+
+```php
+class Foo
+{
+ /** @var string */
+ public $bar;
+}
+```
+
+#### Enums
+
+Values in enums should use PascalCase.
+
+```php
+enum Suit {
+ case Clubs;
+ case Diamonds;
+ case Hearts;
+ case Spades;
+}
+
+Suit::Diamonds;
+```
+
## Naming Convention
-Naming things is often seen as one of the harder things in programming. That's why we've established some high level guidelines for naming classes.
+Naming things is often seen as one of the harder things in programming. That's why we've established some high level
+guidelines for naming classes.
Also, follow naming conventions accepted by Laravel community:
-| What | How | Good | Bad
-|-------|-------|--------|-----------------
-**Controller** | plural | `ArticlesController` | `ArticleController`
-**Route** | plural | `articles/1` | `article/1`
-**Model** | singular | `User` | `Users`
-**Table** | plural | `article_comments` | `article_comment, articleComments`
-**Pivot table** | singular model names | `article_user` | `articles_users`
-**Table column** | snake_case without model name | `meta_title` | `MetaTitle, article_meta_title`
-**Foreign key** | singular model name with _id suffix | `article_id` | `ArticleId, id_article, articles_id`
-**Primary key** | - | `id` | `custom_id`
-**Migration** | - | `2017_01_01_000000_create_articles_table` | `2017_01_01_000000_articles`
-**Method** | camelCase | `getAll` | `get_all`
-**Function** | snake_case | `abort_if` | `abortIf`
-**Method in test class** | camelCase | `testGuestCannotSeeArticle` | `test_guest_cannot_see_article`
-**Model property** | snake_case | `$model->model_property` | `$model->modelProperty`
-**Variable** | camelCase | `$anyOtherVariable` | `$any_other_variable`
-**Collection** | descriptive, plural | `$activeUsers = User::active()->get()` | `$active, $data`
-**Object** | descriptive, singular | `$activeUser = User::active()->first()` | `$users, $obj`
-**Config and language files index** | snake_case | `articles_enabled` | `ArticlesEnabled, articles-enabled`
-**View file name** | kebab-case | `show-filtered.blade.php` | `showFiltered.blade.php, show_filtered.blade.php`
-**Config file name** | kebab-case | `google-calendar.php` | `googleCalendar.php, google_calendar.php`
-**Contract (interface)** | adjective or noun | `Authenticatable` | `AuthenticationInterface, IAuthentication`
-**Trait** | adjective | `Notifiable` | `NotificationTrait`
+| What | How | Good | Bad
+|-------------------------------------|-------------------------------------|-------------------------------------------|---------------------------------------------------
+ **Controller** | plural | `ArticlesController` | `ArticleController`
+ **Route** | plural | `articles/1` | `article/1`
+ **Model** | singular | `User` | `Users`
+ **Table** | plural | `article_comments` | `article_comment, articleComments`
+ **Pivot table** | singular model names | `article_user` | `articles_users`
+ **Table column** | snake_case without model name | `meta_title` | `MetaTitle, article_meta_title`
+ **Foreign key** | singular model name with _id suffix | `article_id` | `ArticleId, id_article, articles_id`
+ **Primary key** | - | `id` | `custom_id`
+ **Migration** | - | `2017_01_01_000000_create_articles_table` | `2017_01_01_000000_articles`
+ **Method** | camelCase | `getAll` | `get_all`
+ **Function** | snake_case | `abort_if` | `abortIf`
+ **Method in test class** | camelCase | `testGuestCannotSeeArticle` | `test_guest_cannot_see_article`
+ **Model property** | snake_case | `$model->model_property` | `$model->modelProperty`
+ **Variable** | camelCase | `$anyOtherVariable` | `$any_other_variable`
+ **Collection** | descriptive, plural | `$activeUsers = User::active()->get()` | `$active, $data`
+ **Object** | descriptive, singular | `$activeUser = User::active()->first()` | `$users, $obj`
+ **Config and language files index** | snake_case | `articles_enabled` | `ArticlesEnabled, articles-enabled`
+ **View file name** | kebab-case | `show-filtered.blade.php` | `showFiltered.blade.php, show_filtered.blade.php`
+ **Config file name** | kebab-case | `google-calendar.php` | `googleCalendar.php, google_calendar.php`
+ **Contract (interface)** | adjective or noun | `Authenticatable` | `AuthenticationInterface, IAuthentication`
+ **Trait** | adjective | `Notifiable` | `NotificationTrait`
### Jobs
@@ -102,30 +154,31 @@ $request->input('name');
Consider using helpers instead of facades. They can clean up your code.
-Common syntax | Shorter and more readable syntax
----------------|------------------------------------
-`Session::get('foo')` | `session('foo')`
-`$request->session()->get('foo')` | `session('foo')`
-`Session::put('foo', $data)` | `session(['foo' => $data])`
-`$request->input('name'),Request::get('name')` | `$request->name,request('name')`
-`return Redirect::back()` | `return redirect()->back()`
-`is_null($object->relation) ? $object->relation->id : null;` | `optional($object->relation)->id`
-`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`
-`$request->has('value') ? $request->value : 'default';` | `$request->get('value','default')`
-`Carbon::now(), Carbon::today()` | `now(), today()`
-`App::make('Class')` | `app('Class')`
-`->where('column', '=', 1)` | `->where('column', 1)`
-`->orderBy('created_at', 'desc')` | `->latest()`
-`->orderBy('age', 'desc')` | `->latest('age')`
-`->orderBy('created_at', 'asc')` | `->oldest()`
-`->select('id', 'name')->get()` | `->get(['id', 'name'])`
-`->first()->name` | `->value('name')`
+ Common syntax | Shorter and more readable syntax
+------------------------------------------------------------------------|----------------------------------------------------
+ `Session::get('foo')` | `session('foo')`
+ `$request->session()->get('foo')` | `session('foo')`
+ `Session::put('foo', $data)` | `session(['foo' => $data])`
+ `$request->input('name'),Request::get('name')` | `$request->name,request('name')`
+ `return Redirect::back()` | `return redirect()->back()`
+ `is_null($object->relation) ? $object->relation->id : null;` | `optional($object->relation)->id`
+ `return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`
+ `$request->has('value') ? $request->value : 'default';` | `$request->get('value','default')`
+ `Carbon::now(), Carbon::today()` | `now(), today()`
+ `App::make('Class')` | `app('Class')`
+ `->where('column', '=', 1)` | `->where('column', 1)`
+ `->orderBy('created_at', 'desc')` | `->latest()`
+ `->orderBy('age', 'desc')` | `->latest('age')`
+ `->orderBy('created_at', 'asc')` | `->oldest()`
+ `->select('id', 'name')->get()` | `->get(['id', 'name'])`
+ `->first()->name` | `->value('name')`
## Docblocks
Don't use docblocks for methods that can be fully type hinted (unless you need a description).
-Only add a description when it provides more context than the method signature itself. Use full sentences for descriptions, including a period at the end.
+Only add a description when it provides more context than the method signature itself. Use full sentences for
+descriptions, including a period at the end.
**Good:**
@@ -181,7 +234,8 @@ Always use fully qualified class names in docblocks.
*/
```
-Using multiple lines for a docblock, might draw too much attention to it. When possible, docblocks should be written on one line.
+Using multiple lines for a docblock, might draw too much attention to it. When possible, docblocks should be written on
+one line.
**Good:**
@@ -212,7 +266,8 @@ If a variable has multiple types, the most common occurring type should be first
## Comments
-Comments should be avoided as much as possible by writing expressive code. If you do need to use a comment, format it like this:
+Comments should be avoided as much as possible by writing expressive code. If you do need to use a comment, format it
+like this:
```php
// There should be a space before a single line comment.
@@ -254,7 +309,8 @@ if (count((array) $builder->getQuery()->joins) > 0)
## Whitespace
-Statements should be allowed to breathe. In general always add blank lines between statements, unless they're a sequence of single-line equivalent operations. This isn't something enforceable, it's a matter of what looks best in its context.
+Statements should be allowed to breathe. In general always add blank lines between statements, unless they're a sequence
+of single-line equivalent operations. This isn't something enforceable, it's a matter of what looks best in its context.
**Good:**
@@ -424,7 +480,8 @@ use Config;
## Traits
-Each applied trait should go on its own line, and the `use` keyword should be used for each of them. This will result in clean diffs when traits are added or removed.
+Each applied trait should go on its own line, and the `use` keyword should be used for each of them. This will result in
+clean diffs when traits are added or removed.
**Good:**
@@ -489,7 +546,8 @@ if ($condition)
### Happy path
-Generally a function should have its unhappy path first and its happy path last. In most cases this will cause the happy path being in an unindented part of the function which makes it more readable.
+Generally a function should have its unhappy path first and its happy path last. In most cases this will cause the happy
+path being in an unindented part of the function which makes it more readable.
**Good:**
@@ -513,7 +571,8 @@ throw new Exception;
### Avoid else
-In general, `else` should be avoided because it makes code less readable. In most cases it can be refactored using early returns. This will also cause the happy path to go last, which is desirable.
+In general, `else` should be avoided because it makes code less readable. In most cases it can be refactored using early
+returns. This will also cause the happy path to go last, which is desirable.
**Good:**
@@ -601,7 +660,7 @@ Configuration files must use kebab-case.
```html
config/
- pdf-generator.php
+pdf-generator.php
```
Configuration keys must use snake_case.
@@ -613,7 +672,8 @@ return [
];
```
-Avoid using the `env` helper outside of configuration files. Create a configuration value from the `env` variable like above.
+Avoid using the `env` helper outside of configuration files. Create a configuration value from the `env` variable like
+above.
**Good:**
@@ -663,7 +723,8 @@ php artisan delete-old-records
php artisan deleteOldRecords
```
-A command should always give some feedback on what the result is. Minimally you should let the `handle` method spit out a comment at the end indicating that all went well.
+A command should always give some feedback on what the result is. Minimally you should let the `handle` method spit out
+a comment at the end indicating that all went well.
```php
// in a Command
@@ -675,7 +736,9 @@ public function handle()
}
```
-When the main function of a result is processing items, consider adding output inside of the loop, so progress can be tracked. Put the output before the actual process. If something goes wrong, this makes it easy to know which item caused the error.
+When the main function of a result is processing items, consider adding output inside of the loop, so progress can be
+tracked. Put the output before the actual process. If something goes wrong, this makes it easy to know which item caused
+the error.
At the end of the command, provide a summary on how much processing was done.
@@ -717,7 +780,8 @@ Minimize usage of vanilla PHP in Blade templates.
## Validation
-When using multiple rules for one field in a form request, avoid using |, always use array notation. Using an array notation will make it easier to apply custom rule classes to a field.
+When using multiple rules for one field in a form request, avoid using |, always use array notation. Using an array
+notation will make it easier to apply custom rule classes to a field.
**Good:**
@@ -771,7 +835,8 @@ $request->validate([
## Eloquent over raw SQL queries
-Eloquent is preferred over Query Builder and raw SQL queries. Eloquent allows you to write code that is both readable and maintainable. Eloquent has great built-in tools like soft deletes, events, scopes etc.
+Eloquent is preferred over Query Builder and raw SQL queries. Eloquent allows you to write code that is both readable
+and maintainable. Eloquent has great built-in tools like soft deletes, events, scopes etc.
**Good:**
@@ -843,6 +908,5 @@ $users = User::with('profile')->get();
$users = User::all()
```
-
___
-The above are sections taken from the [Spatie PHP Code Guidelines](https://spatie.be/guidelines/laravel-php).
\ No newline at end of file
+The above are sections taken from the [Spatie PHP Code Guidelines](https://spatie.be/guidelines/laravel-php).
diff --git a/upgrade-guide.md b/upgrade-guide.md
index 3bcc767..32333a2 100644
--- a/upgrade-guide.md
+++ b/upgrade-guide.md
@@ -1,104 +1,143 @@
---
-title: "Upgrade Guide"
+title: "Upgrade from v3.x"
section: "getting-started"
sortOrder: 30
---
-> As of TastyIgniter 2.0 or later, When a new version of TastyIgniter is available you will receive an update message in your TastyIgniter Admin Panel. All you have to do to update TastyIgniter is to click the **Update** notification button at the right top. After that you will be redirected to the **Update Center** page.
+The following is an instructional guide on how to upgrade your v3.x TastyIgniter installation to the latest version v4.x
-There are two methods for updating - the easiest is through the **Update Center**, which will work in most cases.
-If it doesn't work, or you just prefer to be more hands-on, you can follow the manual update process.
+TastyIgniter has been rewritten as an installable Laravel package. This is a huge change that affects almost
+every part of the codebase.
-## Upgrading TastyIgniter v2.1.x to v3.x.x
+## Backing Up The Database
-This is a huge release. It contains a massive change in the codebase from CodeIgniter to Laravel with some new features
-and plenty bug fixes.
+Use this command to backup your MySQL database. For example, if the username is `root` and the database is called
+`database_name`. You will then be prompted to enter the password.
-Listing all the improvements in v3 would be great but its a lot I lost track. So straight to upgrade!
+```bash
+mysqldump -u root -p database_name > tastyigniter_backup.sql
+```
-1. Follow the back up TastyIgniter steps below to back up your files and database.
-2. Delete all your existing v2+ files
-3. Follow the [quick installation](installation#quick-installation) instructions to install a fresh instance of v3.x.x
-4. Make sure you enter the credentials for the database where your v2 data are stored, to upgrade the data structure.
-5. Access your newly upgraded website.
+To restore the backup, you can use this command.
+```bash
+mysql -u root -p database_name < tastyigniter_backup.sql
+```
+## New requirements
-## Upgrading TastyIgniter v2.0.x to v2.1.x
+- **PHP 8.2+** with the following extensions: bcmath, pdo_mysql, ctype, curl, openssl, dom, gd, exif, mbstring, json,
+ tokenizer, zip, xml
+- **MySQL 5.7+** or **MariaDB 10.3+** or **PostgreSQL 10.0**
-{{alerts.callout_info}}After UPDATE make sure your layout modules are displaying on the storefront. You can use the new layout modules drag and drop under Design > Layouts.{{alerts.end}}
+## Upgrading from v3.x
-## Back up TastyIgniter
+> Before you get started, it's a good idea to back up your website. This means if there are any issues you can restore
+> your website.
-Before you get started, it's a good idea to back up your website. This means if there are any issues you can restore your website.
+Install TastyIgniter 4.0 in a new directory
-### Backing Up Your Database
-Visit your TastyIgniter admin page at /admin. Go to `Maintenance` under `System -> Tools` and `Select tables to backup` then click the `Backup` button. On the next page, make sure `Add DROP TABLE statement` is `No`, click the `Backup` button again and your backed up database file will appear under the `Exisiting Backups` Tab. You can download and keep the file on your computer.
+```bash
+composer create-project tastyigniter/tastyigniter mytasty-new
+```
-### Backing Up Your TastyIgniter Site
-Backup your files using FTP clients or cPanel file manager to copy or create a zip of all the existing TastyIgniter files and folders.
+To make the configuration process run smoothly, you can copy your old configuration to the new website. This step is
+optional
-{{alerts.warning}}The upgrade process will affect all files and folders included in the TastyIgniter installation. This includes all the core files used to run TastyIgniter. If you have made any modifications to those files, your changes will be lost.{{alerts.end}}
+```bash
+cp mytasty/.env mytasty-new/
+```
-### Restoring Your Database From Backup
-Visit your TastyIgniter admin maintenance page. Under `Exisiting Backups` tab click the `Restore` button next to the database backup you wish to restore.
+To avoid broken media, you can either copy or move the `assets/media` directory to the storage directory on the new
+installation.
-## Manual Update
+```bash
+cp assets/media storage/
+```
-If the one-click upgrade doesn't work for you, don't panic! Just try a manual update.
+If you have custom extensions and themes you have developed yourself, you can copy them to the new installation after
+upgrade is complete.
-### **Step 1:** Replace TastyIgniter files
-1. Get the [latest TastyIgniter](https://tastyigniter.com/download) zip file
-2. Unpack the downloaded zip file
-3. Open the unpacked folder `TastyIgniter-2.x.x`
-4. Locate and `delete` the `database.php` file in `system/tastyigniter/config/` folder
-5. Using your FTP client, upload all the files and folders inside the `TastyIgniter-2.x.x` folder to your web host. Uploading all the files might take a few minutes on the FTP client.
-6. Do NOT replace/overwrite your existing `system/tastyigniter/config/database.php` file.
+Proceed through the installation and database migration. Change directory to the new installation and running the
+installation command.
-### **Step 2:** Update your installation
-After uploading the files of the new version to your web host, visit the setup page at "/setup" like: www.myrestaurant.com/setup. Following the instructions on the setup page will update your database to be compatible with the latest code.
+```bash
+cd mytasty-new
+php artisan igniter:install
+```
-{{alerts.warning}}Warning: DO NOT proceed if you see the Database page asking you to enter your database details, this means your old database.php file has changed. Fix this by restoring your old database.php file then return to Step 1{{alerts.end}}
+Finally, complete the upgrade, replace the old installation directory with the new installation.
+```bash
+cd ..
+mv mytasty mytasty-old
+mv mytasty-new mytasty
+```
-### **Step 3:** Do something nice for yourself
-Clear your site (if enabled) and browser cache at this point so the changes will go live immediately. Otherwise, visitors to your site (including you) will continue to see the old version (until the cache updates).
+### High impact changes
-Your TastyIgniter installation is successfully updated..
+#### TastyIgniter As A Package
-{{alerts.warning}}If you experience any issue, you should restore your most recent database & file backup and try again{{alerts.end}}
+TastyIgniter can now be included in an existing Laravel application as a package.
+See [Package Installation](/installation#package-installation) for more details.
-{{alerts.note}}THIS IS FOR UPGRADE ON EXISTING INSTALLS ONLY! IF INSTALLING NEW, BE SURE TO READ THE README.md FILE INSTEAD{{alerts.end}}
+#### Code Structure
+The codebase has been refactored and restructured to follow Laravel conventions which enhance maintainability and
+extensibility. This includes updated namespaces, relocated controllers, improved models, and form requests.
-## Upgrading TastyIgniter v1.4.x to 2.0.x
+- Classes under `Admin\\`, `Main\\`, `System\\` have moved to `Igniter\\Admin\\`, `Igniter\\Main\\`, `Igniter\\System\\`
+ respectively.
-1. BACKUP YOUR EXISTING STORE FILES AND DATABASE!!
- - Backup your database via your store `Admin->Tools->Maintenance->Backup`
- - Backup your files using FTP file copy or use cPanel filemanager to create a zip of all the existing tastyigniter files and folders
+#### Admin Login with Email
-2. Download the latest version of TastyIgniter and upload ALL new files on top of your current install EXCEPT your `system/tastyigniter/config/database.php`.
- - Make sure your `system/tastyigniter/config/database.php` old file was not overwritten.
+The admin login process now uses email addresses instead of usernames, providing a more convenient and familiar login
+experience for administrators.
-3. Go to http://myrestaurant.com/ Replacing myrestaurant.com with your actual site (and subdirectory if applicable).
+#### Mailable Integration
-4. You should see the TastyIgniter Setup script.
+Sending registered mail templates now uses Laravel's Mailable classes instead of custom logic. This provides a more
+standardized and maintainable approach to sending emails.
-5. Click **Continue**. After a few seconds you should see the installation success page.
- - If you see the database configuration and/or site settings TastyIgniter Setup page, then that means you have replaced your old `system/tastyigniter/config/database.php` file. Restore them from your backup first. Then try again.
+#### Mail Template Namespaces
-6. Clear any cookies in your browser
+Mail template namespaces have been renamed for consistency. `admin::` is
+now `igniter.admin::`, `main::` is now `igntier.main::`, and `system::` is now `igniter.system::`.
-7. Go to the administrator panel and login as the main administrator. Press Ctrl+F5 3x times to refresh your browser cache. That will prevent oddly shifted elements due to stylesheet changes.
- - If you see any errors, report them immediately in the forum before continuing.
+#### Translation String Keys
-9. Go to `Admin->System` Settings
- - Update any blank fields and click save.
- - Even if you do not see any new fields, click save anyway to update the database with any new field names.
+Translation string keys have been updated to follow a consistent naming
+convention. `admin::lang.` is now `igniter::admin.`, `main::lang.` is now `igntier::main.`, and `system::lang.` is
+now `igniter::system.`.
+#### The Singleton Trait
-## Troubleshooting
+Singletons have been dropped, you can resolve Manager classes through service containter. This allows for better code
+organization and easier unit testing. `ExtensionManager::instance()` is now `resolve(ExtensionManager::class)`.
-If you have any upgrade script errors, post them in the forum
-You should always visit the forum immediately after a fresh upgrade to see if there are any immediate bug fixes
-If nobody has reported your bug, then please report it.
\ No newline at end of file
+#### Blade Directives
+
+New Blade directives have been introduced to simplify theme development. The `@themeContent` directive
+allows rendering of content template files, while `@themePage` replaces `@page` used for rendering page contents.
+Additionally,
+`@componentPartial` and `@themePartial` directives replace the previous `@component` and `@partial` directives.
+
+### Medium impact changes
+
+#### Extension Configuration
+
+Extension configuration files are no longer merged automatically. Developers must use Laravel's
+mergeConfigFrom() method to merge configuration files from extension class `register` method.
+
+#### Database Changes
+
+Several database changes have been made, including merging `staffs` and `users` records into the `admin_users` table,
+renaming `staff_groups` to `user_groups`, prefixing user-related tables with `admin_`, dropping conflicting tables, and
+increasing the length of varchar to 255 on existing columns.
+
+### Low impact changes
+
+#### Admin Controller Actions
+
+New base view files have been introduced for common admin controller actions such as index, edit, create, and preview.
+This eliminates the need to create these view files for your custom controller action.
From eeebc318c2d9f0220be43479eb84393ee54e8b61 Mon Sep 17 00:00:00 2001
From: Sam Poyigi <6567634+sampoyigi@users.noreply.github.com>
Date: Fri, 12 Apr 2024 13:47:48 +0100
Subject: [PATCH 02/14] wip
Signed-off-by: Sam Poyigi <6567634+sampoyigi@users.noreply.github.com>
---
installation.md | 88 ++++++++++++++++++++++++++++--------------------
upgrade-guide.md | 17 +++++-----
2 files changed, 59 insertions(+), 46 deletions(-)
diff --git a/installation.md b/installation.md
index 9b89450..e104cd6 100644
--- a/installation.md
+++ b/installation.md
@@ -11,7 +11,7 @@ application.
### Requirements
-These are the requirements to run TastyIgniter:
+These are the requirements to run TastyIgniter as a stand-alone application:
- **Apache** (with mod_rewrite enabled) or **Nginx**
- **PHP 8.2+** with the following extensions: bcmath, pdo_mysql, ctype, curl, openssl, dom, gd, exif, mbstring, json,
@@ -35,7 +35,7 @@ From here, you can move on to the [Setting up TastyIgniter](#setting-up-tastyign
### Requirements
-These are the requirements to run TastyIgniter:
+These are the requirements to run TastyIgniter as a package in a Laravel application:
- **Laravel 10+**
- **MySQL 5.7+** or **MariaDB 10.3+** or **PostgreSQL 10.0**
@@ -54,8 +54,8 @@ From here, you can move on to the [Setting up TastyIgniter](#setting-up-tastyign
## Setting up TastyIgniter
-TastyIgniter comes packaged with a command-line setup utility that will get you up and running in a few minutes. It will
-attempt to automatically configure TastyIgniter and create an admin user account.
+TastyIgniter includes a command-line setup tool that will get you up and running in a few minutes. It will attempt to
+set up TastyIgniter and create an admin user account.
In the TastyIgniter installation's root directory, run the following command:
@@ -85,6 +85,16 @@ php artisan igniter:install --no-interaction
There are some things you may need to set up after the installation is complete.
+### Directory permissions
+
+Once TastyIgniter is installed, you must grant the non-root user the necessary permissions so that TastyIgniter and Laravel can write to the required system directories.
+
+```bash
+sudo chmod -R 755 /path/to/tastyigniter
+sudo chown -R www-data:www-data /path/to/tastyigniter
+```
+> You should never set any folder or file to permission level **777**, as this permission level allows anyone to access the content of the folder and file regardless of user or group.
+
### Setting up the task scheduler
You should add the following Cron entry to your server for scheduled tasks to function properly. Crontab editing is
@@ -99,14 +109,15 @@ This Cron will call the command scheduler every minute. When executing the `sche
assess your scheduled tasks and run the tasks that are due.
> Task Scheduling is how scheduling time-based tasks are managed in TastyIgniter. Several core features of TastyIgniter,
-> such as checking for updates, use the scheduler.
+> such as assigning orders and checking for updates, use the scheduler.
### Setting up the queue daemon
By default, the queue in TastyIgniter is synchronous and will attempt to run tasks such as sending emails in real time.
-This behaviour can be set to an asynchronous method by changing the `default` parameter in the `config/queue.php`.
+This behaviour can be set to an asynchronous method by updating the `QUEUE_CONNECTION` variable in your application's
+`.env` file.
-If you are using the `database` queue, it is a good idea to run the queue process as a daemon service. Use the following
+It is a good idea to run the queue process as a daemon service. Use the following
command:
```bash
@@ -126,12 +137,18 @@ can be found below.
### Apache configuration
-There are some extra system requirements if your webserver is running Apache, `mod_rewrite` should be installed and
-enabled and the `AllowOverride` option should be switched on.
-
TastyIgniter includes a `.htaccess` file - make sure it's been uploaded correctly.
+**There are some extra system requirements if your webserver is running Apache, `mod_rewrite` should be installed and
+enabled and the `AllowOverride` option should be switched on.**
+
+```apache
+
+ AllowOverride All
+
+```
-You will need to uncomment this line in the `.htaccess` file in some cases:
+You will need to uncomment this line in
+the [`.htaccess`](https://github.com/tastyigniter/TastyIgniter/blob/master/public/.htaccess) file in some cases:
```html
## !IMPORTANT! You may need to uncomment the following line for some hosting environments,
@@ -140,7 +157,7 @@ You will need to uncomment this line in the `.htaccess` file in some cases:
# RewriteBase /
```
-If you've created a subdirectory, you can add the subdirectory name as well:
+If you've created a subdirectory, you can specify the subdirectory name as well:
```html
RewriteBase /mysubdirectory/
@@ -148,7 +165,8 @@ RewriteBase /mysubdirectory/
### Nginx configuration
-Make sure that `.nginx.conf` file included with TastyIgniter has been uploaded correctly. Then, assuming you have Nginx
+Make sure that [`.nginx.conf`](https://github.com/tastyigniter/TastyIgniter/blob/v4/.nginx.conf) file included with
+TastyIgniter has been uploaded correctly. Then, assuming you have Nginx
setup, add the following to your server's configuration block:
```html
@@ -159,24 +177,22 @@ As an example, your site conf file should look something like:
```html
server {
-
-listen 80;
-
-root /path/to/tastyigniter;
-index index.php;
-
-server_name mytastysite.com;
-
-gzip on;
-gzip_proxied expired no-cache no-store private auth;
-gzip_types text/plain text/css application/x-javascript application/json application/javascript image/x-icon image/png image/gif image/jpeg image/svg+xml;
-
-charset utf-8;
-
-access_log off;
-
-include /path/to/tastyigniter/.nginx.conf;
-
+ listen 80;
+
+ root /path/to/tastyigniter;
+ index index.php;
+
+ server_name mytastysite.com;
+
+ gzip on;
+ gzip_proxied expired no-cache no-store private auth;
+ gzip_types text/plain text/css application/x-javascript application/json application/javascript image/x-icon image/png image/gif image/jpeg image/svg+xml;
+
+ charset utf-8;
+
+ access_log off;
+
+ include /path/to/tastyigniter/.nginx.conf;
}
```
@@ -184,14 +200,13 @@ include /path/to/tastyigniter/.nginx.conf;
### Debug mode
-The debug setting is found in the `config/app.php` configuration file with the `debug` parameter, and is disabled by
-default.
+The debug setting is found in the `config/app.php` configuration file with the `debug` parameter, and is disabled by default.
When enabled, this setting will display detailed error messages when they occur along with other debugging functions.
Debug mode should always be disabled in a live production site. This prevents the display of potentially sensitive
information to the end user.
-> Important: Always set the `APP_DEBUG` setting to false in production environments.
+> **Important:** Always set the `APP_DEBUG` setting to false in production environments.
### CSRF protection
@@ -200,8 +215,7 @@ TastyIgniter offers a simple method to protect your application from cross-site
For every active user session managed by the application, TastyIgniter automatically generates a CSRF "token." This
token is used to check that the authenticated user is the one who actually makes the client requests.
-Although CSRF security is enabled by default, you can disable it in the `config/system.php` configuration file using
-the `enableCsrfProtection` parameter.
+Although CSRF security is enabled by default, you can disable it by updating the `ENABLE_CSRF` variable in your application's `.env` file.
## Getting Started
@@ -230,4 +244,4 @@ After you've logged in you'll be able to access the administration panel to conf
3. **Setup successful but storefront links are not working:** Check that the theme's required extensions are all
installed.
-> **Note:** A detailed installation log can be found in the `setup/setup.log` file.
+> **Note:** A detailed log can be found in the `storage/logs/laravel.log` file.
diff --git a/upgrade-guide.md b/upgrade-guide.md
index 32333a2..2de628c 100644
--- a/upgrade-guide.md
+++ b/upgrade-guide.md
@@ -94,11 +94,6 @@ extensibility. This includes updated namespaces, relocated controllers, improved
The admin login process now uses email addresses instead of usernames, providing a more convenient and familiar login
experience for administrators.
-#### Mailable Integration
-
-Sending registered mail templates now uses Laravel's Mailable classes instead of custom logic. This provides a more
-standardized and maintainable approach to sending emails.
-
#### Mail Template Namespaces
Mail template namespaces have been renamed for consistency. `admin::` is
@@ -107,17 +102,17 @@ now `igniter.admin::`, `main::` is now `igntier.main::`, and `system::` is now `
#### Translation String Keys
Translation string keys have been updated to follow a consistent naming
-convention. `admin::lang.` is now `igniter::admin.`, `main::lang.` is now `igntier::main.`, and `system::lang.` is
+convention. `admin::lang.` is now `igniter::admin.`, `main::lang.` is now `igniter::main.`, and `system::lang.` is
now `igniter::system.`.
#### The Singleton Trait
-Singletons have been dropped, you can resolve Manager classes through service containter. This allows for better code
+Singletons have been dropped, you can resolve all 'Manager' classes through the service container. This allows for better code
organization and easier unit testing. `ExtensionManager::instance()` is now `resolve(ExtensionManager::class)`.
#### Blade Directives
-New Blade directives have been introduced to simplify theme development. The `@themeContent` directive
+New Blade directives have been introduced to simplify theme development and avoid conflicts. The `@themeContent` directive
allows rendering of content template files, while `@themePage` replaces `@page` used for rendering page contents.
Additionally,
`@componentPartial` and `@themePartial` directives replace the previous `@component` and `@partial` directives.
@@ -138,6 +133,10 @@ increasing the length of varchar to 255 on existing columns.
### Low impact changes
#### Admin Controller Actions
-
New base view files have been introduced for common admin controller actions such as index, edit, create, and preview.
This eliminates the need to create these view files for your custom controller action.
+
+#### Mailable Integration
+Sending registered mail templates now uses Laravel's Mailable classes instead of custom logic. This provides a more
+standardized and maintainable approach to sending emails.
+
From 953cf84275d6f26c6813782fe31decd897cd7895 Mon Sep 17 00:00:00 2001
From: Sam Poyigi <6567634+sampoyigi@users.noreply.github.com>
Date: Thu, 25 Apr 2024 01:13:35 +0100
Subject: [PATCH 03/14] Complete docs
---
advanced/ajax-request.md | 450 +++++-
advanced/localization.md | 236 ++--
advanced/mail.md | 232 ++-
advanced/routing.md | 8 -
advanced/scheduling-tasks.md | 63 +-
.../{testing-tastyigniter.md => testing.md} | 4 +-
advanced/validation.md | 282 ++++
customize/components.md | 567 +++++++-
customize/layouts.md | 177 ++-
customize/markup-guide.md | 233 ++--
customize/media-files.md | 26 -
customize/media-manager.md | 142 ++
customize/pages.md | 150 +-
customize/partials.md | 50 +-
customize/permissions.md | 84 --
customize/themes.md | 257 ++--
extend/building-components.md | 257 ----
extend/controllers.md | 233 +++-
extend/extensions.md | 458 +++---
extend/form-requests.md | 31 -
extend/forms.md | 1238 +++++++++++++++++
extend/lists.md | 692 +++++++++
extend/permissions.md | 109 ++
extend/widgets.md | 338 ++++-
installation.md | 48 +-
.../code-of-conduct.md | 2 -
.../contribution-guide.md | 78 +-
.../js-coding-guidelines.md | 0
.../php-coding-guidelines.md | 82 +-
upgrade-guide.md | 7 +-
30 files changed, 5353 insertions(+), 1181 deletions(-)
delete mode 100644 advanced/routing.md
rename advanced/{testing-tastyigniter.md => testing.md} (66%)
create mode 100644 advanced/validation.md
delete mode 100644 customize/media-files.md
create mode 100644 customize/media-manager.md
delete mode 100644 customize/permissions.md
delete mode 100644 extend/building-components.md
delete mode 100644 extend/form-requests.md
create mode 100644 extend/forms.md
create mode 100644 extend/lists.md
create mode 100644 extend/permissions.md
rename code-of-conduct.md => resources/code-of-conduct.md (99%)
rename contribution-guide.md => resources/contribution-guide.md (66%)
rename js-coding-guidelines.md => resources/js-coding-guidelines.md (100%)
rename php-coding-guidelines.md => resources/php-coding-guidelines.md (72%)
diff --git a/advanced/ajax-request.md b/advanced/ajax-request.md
index 41220ac..ec0db2f 100644
--- a/advanced/ajax-request.md
+++ b/advanced/ajax-request.md
@@ -1,12 +1,456 @@
---
title: "Handling AJAX Requests"
section: advanced
-sortOrder: 320
-callout: This section is incomplete. Please help to improve it.
+sortOrder: 300
---
## Introduction
+TastyIgniter provides a simple and easy way to make AJAX requests to the server using the `$.request` JavaScript object. This object is a wrapper around the jQuery AJAX function, providing a more convenient way to make AJAX requests.
+
+In this guide, you'll learn how to make AJAX requests using the `$.request` object, handle AJAX requests on the server, and customize AJAX requests using options.
+
+Alternatively, you can use [Livewire's Event Listeners](https://livewire.laravel.com/docs/actions#event-listeners) to make AJAX requests.
+
## How AJAX requests work
-## Usage
\ No newline at end of file
+The `$.request` object is a wrapper around the jQuery AJAX function, providing a more convenient way to make AJAX requests. It is used to send an HTTP request to the server and receive a response from the server without reloading the page.
+
+Here's an example of how to make an AJAX request using the `$.request` object:
+
+```javascript
+$.request('onSave', {
+ data: {
+ var1: 'some string',
+ var2: 'another string'
+ },
+ success: function(data) {
+ console.log(data);
+ }
+});
+```
+
+In this example, the `$.request` object is used to send an AJAX request to the server. The first argument is the name of the handler method on the server that will handle the request. The second argument is an object containing the request data and a callback function to handle the response.
+
+You can use data attributes to trigger AJAX requests by adding the `data-request` attribute to HTML elements. The value of the `data-request` attribute should be the name of the handler method on the server that will handle the request.
+
+```blade
+
+```
+
+## Using AJAX handlers
+
+To handle an AJAX request on the server, you need to create a handler method in your controller that will process the request and return a response. The handler method should be named `on{HandlerName}`.
+
+Here's an example of a handler method:
+
+```php
+public function onSave()
+{
+ $param1 = post('param1');
+ $param2 = post('param2');
+
+ // Process the request data
+
+ return ['result' => 'success'];
+}
+```
+
+You can pass data to the server using the `data` option in the `$.request` object. The data should be an object containing key-value pairs of the request data.
+
+Here's an example of how to pass data to the server in an AJAX request:
+
+```javascript
+$.request('onSave', {
+ data: {
+ param1: 'value1',
+ param2: 'value2'
+ },
+ success: function(data) {
+ console.log(data);
+ }
+});
+```
+
+In this example, the `data` option is used to pass data to the server in the AJAX request.
+
+## Handling the response
+
+You can handle the response from the server using the `success` option in the `$.request` object. The `success` option should be a callback function that will be called when the server returns a response. Additionally, you can use the `done` promise method to handle errors.
+
+Here's an example of how to handle the response from the server in an AJAX request:
+
+```javascript
+$.request('onSave', {
+ success: function(data) {
+ console.log(data);
+ }
+});
+```
+
+And here's how you can handle the response using the `done` promise method:
+
+```javascript
+$.request('onSave').done(function(data) {
+ console.log(data);
+});
+```
+
+## Error handling
+
+You can handle errors that occur during the AJAX request using the `error` option in the `$.request` object. The `error` option should be a callback function that will be called when an error occurs during the request. Additionally, you can use the `fail` promise method to handle errors.
+
+Here's an example of how to handle errors in an AJAX request:
+
+```javascript
+$.request('onSave', {
+ error: function(xhr, status, error) {
+ console.log('An error occurred: ' + error);
+ }
+});
+```
+
+And here's how you can handle errors using the `fail` promise method:
+
+```javascript
+$.request('onSave').fail(function(xhr, status, error) {
+ console.log('An error occurred: ' + error);
+});
+```
+
+## Request options
+
+The `$.request` object supports additional options for customizing AJAX requests. These options can be defined either programmatically using JavaScript or through the data attributes API by adding data attributes to HTML elements.
+
+Some of the common options include:
+
+### `update`
+
+_(Object)_ Specifies a list of partials and page elements to be updated with the response data. The key is the partial name, and the value is the CSS selector of the target element to be updated.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ update: {
+ 'partial1': '#element1',
+ 'partial2': '#element2'
+ }
+});
+```
+**Data attribute**
+```html
+
+```
+
+> You may prepend the CSS Selector with `@` to append contents to the element, `^` to prepend and `~` to replace with.
+
+### `confirm`
+
+_(string)_ Specifies a confirmation message that will be displayed to the user before sending the request. If the user confirms the request, the request will be sent; otherwise, the request will be canceled.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ confirm: 'Are you sure?'
+});
+```
+**Data attribute:**
+```html
+
+```
+
+### `data`
+
+_(Object)_ Specifies the data to be sent with the request. The data should be an object containing key-value pairs of the request data.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ data: {
+ param1: 'value1',
+ param2: 'value2'
+ }
+});
+```
+**Data attribute:**
+```html
+
+```
+
+### `redirect`
+
+_(string)_ Specifies the URL to redirect to after the request is completed.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ redirect: '/success'
+});
+```
+**Data attribute:**
+```html
+
+```
+
+### `headers`
+
+_(Object)_ Specifies additional headers to be sent with the request.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ headers: {
+ 'X-CSRF-TOKEN': 'token'
+ }
+});
+```
+
+### `attach-loading`
+
+_(string)_ Specifies the CSS selector to add to the target element while the request is loading. The attribute value is optional, if not provided, the target element will be disabled.
+
+**Data attribute:**
+```html
+
+```
+
+### `replace-loading`
+
+_(string)_ Specifies the CSS selector to replace on the target element while the request is loading.
+
+**Data attribute:**
+```html
+
+```
+
+### `beforeUpdate`
+
+_(function)_ Specifies a callback function or Javascript code to be executed before updating the target element with the response.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ beforeUpdate: function(data) {
+ console.log('Before update');
+ }
+});
+```
+**Data attribute**
+```html
+
+```
+
+### `success`
+
+_(function)_ Specifies a callback function or Javascript code to be executed after the request is successful.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ success: function(data) {
+ console.log('Success');
+ }
+});
+```
+**Data attribute**
+```html
+
+```
+
+### `error`
+
+_(function)_ Specifies a callback function or Javascript code to be executed when an error occurs during the request.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ error: function(xhr, status, error) {
+ console.log('An error occurred: ' + error);
+ }
+});
+```
+**Data attribute**
+```html
+
+```
+
+### `complete`
+
+_(function)_ Specifies a callback function or Javascript code to be executed after the request is completed.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ complete: function(xhr, status) {
+ console.log('Request completed');
+ }
+});
+```
+**Data attribute**
+```html
+
+```
+
+### `submit`
+
+When set to `true`, the form will be submitted using the default form submission method.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ submit: true
+});
+```
+**Data attribute**
+```html
+
+```
+
+### `form`
+
+_(CSS Selector)_ Specifies the form element to be submitted. Useful when the request is triggered by a button outside the form.
+
+**JavaScript:**
+```javascript
+$.request('onSave', {
+ form: '#myForm'
+});
+```
+**Data attribute**
+```html
+
+```
+
+## Global AJAX events
+
+The AJAX framework triggers several events on the updated elements, the triggering element, form, and the window object. These events are triggered regardless of which API was used - the data attributes API or the JavaScript API.
+
+### `ajaxBeforeSend`
+
+Triggered on the window object before the AJAX request is sent. The handler receives the context object as an argument.
+
+```javascript
+$(window).on('ajaxBeforeSend', function(context) {
+ console.log(context);
+});
+```
+
+### `ajaxBeforeUpdate`
+
+Triggered on the form element immediately after the request is completed but before updating the page. The event handler receives the `event` object, the `context` object, the `data` object received from the server, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$('form').on('ajaxBeforeUpdate', function(event, context, data, status, jqXHR) {
+ console.log(event, context, data, status, jqXHR);
+});
+```
+
+### `ajaxUpdate`
+
+Triggered on the page element after updating the element with the response data. The event handler receives the `event` object, the `context` object, the `data` object received from the server, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$('#element').on('ajaxUpdate', function(event, context, data, status, jqXHR) {
+ console.log(event, context, data, status, jqXHR);
+});
+```
+
+### `ajaxUpdateComplete`
+
+Triggered on the window object after all element are updated. The event handler receives the `event` object, the `context` object, the `data` object received from the server, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$(window).on('ajaxUpdateComplete', function(event, context, data, status, jqXHR) {
+ console.log(event, context, data, status, jqXHR);
+});
+```
+
+### `ajaxSuccess`
+
+Triggered on the form element after the request is successful. The event handler receives the `event` object, the `context` object, the `data` object received from the server, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$('form').on('ajaxSuccess', function(event, context, data, status, jqXHR) {
+ console.log(event, context, data, status, jqXHR);
+});
+```
+
+### `ajaxError`
+
+Triggered on the form element when an error occurs during the AJAX request. The event handler receives the `event` object, the `context` object, the `error` message, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$('form').on('ajaxError', function(event, context, error, status, jqXHR) {
+ console.log(event, context, data, status, jqXHR);
+});
+```
+
+### `ajaxErrorMessage`
+
+Triggered on the window object when an error occurs during the AJAX request. The event handler receives the `event` object and `error` message as an argument.
+
+```javascript
+$(window).on('ajaxErrorMessage', function(event, error) {
+ console.log(event, error);
+});
+```
+
+### `ajaxConfirmMessage`
+
+Triggered on the window object when a `confirm` option is given. The event handler receives the event object and the confirmation message as arguments.
+
+```javascript
+$(window).on('ajaxConfirmMessage', function(event, message) {
+ console.log(event, message);
+});
+```
+
+### `ajaxSetup`
+
+Triggered on the triggering element before the AJAX request is formed. The event handler receives the `context` object as an argument.
+
+```javascript
+$('#element').on('ajaxSetup', function(context) {
+ console.log(context);
+});
+```
+
+### `ajaxPromise`
+
+Triggered on the triggering element before the AJAX request is sent. The event handler receives the `context` object as an argument.
+
+```javascript
+$('#element').on('ajaxPromise', function(context) {
+ console.log(context);
+});
+```
+
+### `ajaxDone`
+
+Triggered on the triggering element when the AJAX request is successful. The event handler receives the `context` object, the `data` object received from the server, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$('#element').on('ajaxDone', function(context, data, textStatus, jqXHR) {
+ console.log(data);
+});
+```
+
+### `ajaxFail`
+
+Triggered on the triggering element when the AJAX request fails. The event handler receives the `context` object, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$('#element').on('ajaxFail', function(context, textStatus, jqXHR) {
+ console.log(jqXHR, textStatus, errorThrown);
+});
+```
+
+### `ajaxAlways`
+
+Triggered on the triggering element when the AJAX request is completed. The event handler receives the `context` object, the `data` object received from the server, the `status` text string, and the `jqXHR` object as arguments.
+
+```javascript
+$('#element').on('ajaxAlways', function(context, dataOrXhr, textStatus, xhrOrError) {
+ console.log(jqXHR, textStatus);
+});
+```
diff --git a/advanced/localization.md b/advanced/localization.md
index 79569d9..1b5587a 100644
--- a/advanced/localization.md
+++ b/advanced/localization.md
@@ -1,159 +1,225 @@
---
title: "Localization"
section: advanced
-sortOrder: 300
+sortOrder: 310
---
## Introduction
-TastyIgniter has a powerful translation system that allows your site to support multilingual content. When developing your extension, you should consider using locale strings even if you do not intend to use it in more than one language. You never know: if you decide to make your extension available to users around the world, it can be handy later.
+TastyIgniter features a robust translation system that leverages Laravel's localization features, providing a convenient method for retrieving strings in various languages, allowing you to easily support multiple languages in your TastyIgniter application.
-We strongly recommend including a complete set of English resources with each extension, also if you decide to publish your extension in the TastyIgniter marketplace.
-
-Locale files are stored within the extension **/language** subdirectory.
+Language directories and files are stored in PHP files within your application's **lang** directory.
## Directory structure
-Here is an example of the extension `language` directory:
+Here is an example of how the **lang** directory might be structured:
```yaml
-extensions/
- igniter/
- demo/ <=== Extension directory
- language/ <=== Localization directory
- en/ <=== Language directory
- default.php <=== Locale file
- es/
- default.php
+ lang/
+ en/ <=== Language directory
+ custom.php <=== Language file
+ es/ <=== Language directory
+ custom.php <=== Language file
```
-> Each language directory should be named using the ISO 639-1 code for the language it contains.
+In this example, the language directory contains language files. Each language has its own subdirectory named using the [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) code (e.g., `en` for English, `es` for Spanish), and each subdirectory contains a `custom.php` language file with the translation strings for that language.
-## Defining locale strings
+## Defining language strings
-All locale files return an array of keyed locale strings. For example:
+All language files return an array of keyed language strings. For example:
```php
'This is a sample locale string.',
- 'alert' => [ // Namespacing for alerts locale strings
+ 'sample_key' => 'This is a sample language string.',
+ 'alert' => [ // Namespacing for alerts language strings
'success' => 'This is a success alert'
]
];
```
-## Accessing locale strings
+## Accessing language strings
-You can use the `lang` helper function to get strings from locale files. The method accepts the file and key of the locale string as its first argument. For example, let's the `sample_key` locale string from the locale file `extensions/igniter/demo/language/en/lang.php`:
+You can use the `Lang` facade, `@lang` Blade directive, `__` helper function, or `lang` helper function to retrieve strings from language files. For instance, to retrieve the `sample_key` language string from the `lang/en/custom.php` language file, you can use any of these methods.
```php
-@lang('igniter.demo::lang.sample_key')
+// Using the `Lang` facade
+echo Lang('custom.sample_key')
+
+// Using the `@lang` Blade directive
+@lang('custom.sample_key')
+
+// Using the `lang` helper function
+echo lang('custom.sample_key')
+
+// Using the `lang` helper function in a Blade template
+{{ lang('custom.sample_key') }}
-@lang('igniter.demo::lang.alert.success')
+// Using the `__` helper function
+echo __('custom.sample_key');
+
+// Using the `__` helper function in a Blade template
+{{ __('custom.sample_key') }}
```
-## Overriding locale strings
+You can also retrieve nested language strings using dot notation. For example, to retrieve the `success` language string from the `alert` namespace in the `lang/en/custom.php` language file:
-System users can override extension locale strings without altering the extension files to modify these strings. For
-example, you should create the locale file `default.php` at the following location to override the locale
-string `sample_key` within the `default.php` file of the `igniter/demo` extension:
+```php
+// Using the `Lang` facade
+{{ __('custom.alert.success') }}
+```
-> You can also easily edit core and/or extension locale strings from the admin interface on the **Localization > Languages > Translations** page
+### Replacing parameters in translation strings
-```yaml
-language/ <=== Localization directory
- en/ <=== Language directory
- igniter/
- demo/ <=== Extension directory
- default.php <=== Locale override file
+If you wish, you can define placeholders in your language strings. All placeholders are prefixed with a `:`. For example, you can define a welcome message with a placeholder name like so:
+
+```php
+ 'Welcome, :name',
+];
+```
+
+To replace the placeholders when retrieving a language string, you may pass an array of replacements as the second argument to the `__` function:
+
+```php
+{{ __('custom.welcome', ['name' => 'dayle']) }}
```
-You should only define the locale strings you want to override in this file. Any locale strings you do not override are still loaded from the original locale files of the extension.
+If your placeholder contains all capital letters, or only has its first letter capitalized, the translated value will be capitalized accordingly:
```php
'This is an overidden locale string.',
+ 'welcome' => 'Welcome, :NAME', // Welcome, DAYLE
+ 'goodbye' => 'Goodbye, :Name', // Goodbye, Dayle
];
```
-## Making your site multilingual
+### Pluralization with language strings
-In this section of the article, you will find a complete overview of the steps involved in the creation of a multilingual TastyIgniter site.
+Pluralization can be a complex issue due to the various rules across different languages. However, Laravel provides a solution to translate strings differently based on your defined pluralization rules. You can distinguish between singular and plural forms of a string using the `|` character, like this:
-### Manually download a Language Pack
+```
+'apples' => 'There is one apple|There are many apples',
+```
-Follow these steps to manually download a community translated language pack.
+You can create even more complex pluralization rules, specifying translation strings for multiple value ranges:
-- Join our translations Crowdin project page.
-- Choose the language you wish to install. For example, let's choose **Spanish (ES)**.
-- Download and unzip the language pack. To download, you need to click on the button at the top right of the Crowdin language page.
-- The folder and file structure of the extracted language pack should look like this.
+```
+'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many',
+```
+Once you've defined a translation string with pluralization options, you can use the `trans_choice` function to retrieve the line for a given "count". For example:
+```php
+{{ trans_choice('custom.apples', 10) }}
+```
+
+You can also define placeholder attributes in pluralization strings. These placeholders can be replaced by passing an array as the third argument to the `trans_choice` function:
-```yaml
-TastyIgniter (es)/
- master/
- es-ES/ <=== Language directory
- master/ <=== Branch directory
- app/ <=== Namespaced directory
- admin/
- lang.php <=== Locale file
- main/
- system/
- extensions/ <=== Namespaced directory
- igniter/
- demo/
- default.php <=== Locale file
```
+'minutes_ago' => '{1} :value minute ago|[2,*] :value minutes ago',
+
+{{ trans_choice('custom.minutes_ago', 5, ['value' => 5]) }} // 5 minutes ago
+```
+
+If you want to display the integer value passed to the `trans_choice` function, you can use the built-in `:count` placeholder:
+
+```
+'apples' => '{0} There are none|{1} There is one|[2,*] There are :count',
+{{ trans_choice('custom.minutes_ago', 5, ['value' => 5]) }} // There are 5
+```
+
+## Overriding language strings
+You can customize all application language strings from the **System > Languages > Edit > Translations** admin page.
-- Copy the files and folders within the `Namespaced` directories into your TastyIgniter `language` directory, `see below`. If you don't have a `language` directory in your application root, create a new one.
+Some TastyIgniter extensions, themes and Laravel packages come with their own language files. If you need to customize these strings without modifying the package's core files, you can override them by placing files in the `lang/vendor/{package}/{locale}` directory.
-
+For instance, if you want to modify the language string `override_key` in `custom.php` for an extension named `acme.helloworld`, you should create a language file `lang/vendor/acme-helloworld/en/custom.php` within the root of your TastyIgniter installation.
```yaml
-language/
- es_ES/ <=== Language directory
- admin/
- lang.php <=== Locale file
- main/
- system/
- igniter/
- demo/
- default.php <=== Locale file
+lang/
+ vendor/
+ acme-helloworld/
+ en/ <=== Language directory
+ custom.php <=== Language override file
+```
+
+In this file, define only the language strings you want to change. Any strings you don't override will still be loaded from the original language file.
+
+```php
+ 'This is an overidden language string.',
+];
+```
+
+## Making your site multilingual
+
+In this section, we'll guide you through the steps required to make your TastyIgniter site multilingual. There are two main ways to install additional languages: directly from the **System > Languages** page in the admin interface, or manually downloading a language pack from the TastyIgniter translations Crowdin project page.
+
+### Installing a language pack
+
+TastyIgniter comes with a default language pack for the English language. You can install additional language packs from the **System > Languages** page of the admin interface.
+
+- Navigate to the **System > Languages** page in the admin interface.
+- Using the searchbar at the top of the page, search for the language you wish to install. For example, let's search for **Spanish (ES)**.
+- Click on the language you wish to install, then click the **Add Language** button.
+- Once installed, you can enable the language by toggling the **Status** switch to `Enabled`.
+
+Run the following command from the application directory to install a **Spanish (ES)** language pack:
+
+```bash
+php artisan igniter:language-install es
```
-> Notice the language directory name uses **underscore** instead of an hypen (e.g. “es_ES”).
+### Manually download a language pack
-### Installing a Language Pack
+Follow these steps to manually download a community translated language pack.
+
+- Join our translations Crowdin project page.
+- Choose the language you wish to install. For example, let's choose **Spanish (ES)**.
+- Download and unzip the language pack. To download, you need to click on the button at the top right of the Crowdin language page.
+- The extracted language pack should have a specific folder and file structure. Copy the files and folders within the `Namespaced` directories into your TastyIgniter `lang` directory, _see below_. If you don't have a `lang` directory in your application root, create a new one.
-After downloading a language pack, you must create a new language in the admin interface to register the language into the system.
+```yaml
+lang/
+ vendor/
+ igniter/
+ es_ES/ <=== Language directory
+ admin.php
+ main.php
+ system.php
+```
-1. Create a new language from the **Localisation > Languages** page of the admin interface.
-2. Fill in the form. The value of the `Locale Code` field must match the language directory name. Using the example above, the value will be `es_ES`
-3. Lastly, toggle the **Status** switch to `Enabled` then save the form.
+> Notice the language directory name uses underscore `_` instead of a hyphen `-` (e.g. "es_ES").
-### Setting the Default Language
+### Configuring the default language
-You may want to set the installed language pack as the default language for new users and visitors. You can do this on
-the **System > Settings > General** page of the admin interface, under the **Site** tab select the language you want to
-use as your default.
+You may want to set the installed language pack as the default language for new users and visitors. You can do this on the **System > Languages** page of the admin interface, by clicking the **Set as Default** button next to the language you want to use as the default.
### Enabling language detection
-TastyIgniter includes support for language negotiation out-the-box using a variety of methods without forcing the user to choose his or her language. The language is detected in the following sequence:
+TastyIgniter comes with built-in support for language negotiation, offering various methods to automatically detect the user's language without requiring manual selection. The language detection process follows this sequence:
-- Determine the language from the **request/session** parameter.
-- Determine the language from the user browser's language settings.
+- **On the Admin Interface**
+ - Determine the Admin Interface language from currently logged in admin user language settings.
+ - Determine the language from the user browser's language settings.
+ - Determine the language from the **default** language setting.
+- **On the FrontEnd**
+ - Determine the frontend language from the **request/session** parameter.
+ - Using the locale picker provided within the [FrontEnd extension](..extensions/frontend#locale-picker).
+ - Determine the language from the **default** language setting.
-## Third-Party Extensions
+## Translating third-party extensions
-While language packs downloaded from the **Browse Languages** page of the admin interface may usually provide translations for all recommended extensions bundled with TastyIgniter, as a rule, they will not cover any third party extensions that you may have installed. Developers are responsible for providing and maintaining translations of their extensions.
+Language packs downloaded from the **System > Languages** page in the admin interface typically include translations for all TastyIgniter recommended extensions. However, it's important to note that they may be missing translations for other extensions you've installed.
-So before you install a third-party extension, you should check to make sure it includes translations for each language pack you have installed. If you find that an extension doesn't support a language you need, please contact the developer directly and arrange to have the necessary translations added.
+Developers of TastyIgniter extensions are responsible for providing and maintaining translations for their extensions. Before installing a third-party extension, ensure that it includes translations for each language pack you have installed. If you find that an extension doesn't support a language you need, please contact the developer directly to arrange for the necessary translations to be added.
\ No newline at end of file
diff --git a/advanced/mail.md b/advanced/mail.md
index 61ec807..66ec2af 100644
--- a/advanced/mail.md
+++ b/advanced/mail.md
@@ -1,34 +1,246 @@
---
title: "Mail"
section: advanced
-sortOrder: 305
-callout: This section is incomplete. Please help to improve it.
+sortOrder: 320
---
## Introduction
+TastyIgniter uses the Laravel Mail component to send emails. The Mail component provides a clean, simple API which allows you to send emails through a variety of drivers, including SMTP, Mailgun, Postmark, Amazon SES.
+
### Driver prerequisites
+Before using the Mailgun, SparkPost or SES drivers you will need to install [Drivers extension](https://tastyigniter.com/marketplace/item/igniter-drivers).
+
+## Configuration
+
+The mail configuration file is located at `config/mail.php`. In this file, you may configure the default mail driver, mail sending options, and mail "from" address. Next, verify that your `config/services.php` configuration file contains the required credentials for your mail service.
+
## Writing mail
-### Using mail templates
+In TastyIgniter, you can send mail messages using either mail templates or mail views. Mail templates can be managed through the **Design > Mail templates** admin page. On the other hand, mail views supplied by the application or extension are stored in the `resources/views` directory within the extension's directory.
+
+Optionally, you can [register mail views in the Extension class](../advanced/mail#registering-mail-templates-layouts--partials) with the `registerMailTemplates` method. This enables automatic generation of mail templates for easy customization via the admin interface.
+
+> All mail messages support using Blade and Markdown syntax for markup.
+
+### Creating mail views
+
+Mail views are stored in the file system, and the _mail code_ is used to represent the path to the view file. For example, sending mail with the code `vendor.extension::mail.message` would use the content in the corresponding file at `vendor/extension/views/mail/message.blade.php`.
+
+The mail view file content can include up to 3 sections: **configuration**, **plain text**, and **HTML markup**. These sections are separated using the `==` sequence. For example:
+
+```blade
+subject = "Your order has been placed"
+==
+Hello {{ $customer->first_name }},
+
+Your order has been placed successfully.
+
+Thank you for your order.
+==
+
Hello {{ $customer->first_name }},
+
+
Your order has been placed successfully.
+
+
Thank you for your order.
+```
+
+The **configuration** section sets the mail view parameters. The following configuration parameters are supported:
+
+| Parameter | Description |
+|------------------|------------------|
+| `subject` | the mail message subject, **required**. |
+| `layout` | the mail layout code, **optional**. Default value is default. |
+
+The **plain text** section is optional, while the **configuration** and **HTML markup** sections are required.
+
+```blade
+subject = "Your order has been placed"
+==
+
Hello {{ $customer->first_name }},
+
+
Your order has been placed successfully.
+
+
Thank you for your order.
+```
+
+### Creating mail templates
+
+Mail templates are used to define the structure of the mail message. You can create mail templates in the admin interface by navigating to _Design > Mail templates_. The `code` specified in the template is a unique identifier and cannot be changed once created.
+
+Here is an example of a simple mail template:
+
+```blade
+subject: Your order has been placed
+==
+
Hello {{ $customer->first_name }},
+
+
Your order has been placed successfully.
+
+
Thank you for your order.
+```
+
+### Creating mail layouts
+
+Mail layouts can be created by navigating to _Design > Mail templates > Layouts_ in the admin interface. Mail layouts are used to define the structure of the mail message, including the header, footer, and other common elements. The `code` specified in the layout is a unique identifier and cannot be changed once created.
+
+By default, TastyIgniter comes with a `default` mail layout that can be used as a starting point for creating custom mail layouts.
+
+Here is an example of a simple mail layout:
+
+```blade
+
+
+
+
+
+
+
+
+
+
{{ $subject }}
+
-### Using mail layouts
+
+ {!! $body !!}
+
-### Using mail partials
+
+
+
+```
+
+In this example, the layout includes a header, main content, and footer sections. The `{{ $custom_css }}` variable is used to include custom CSS styles in the mail layout, and the `{{ $layout_css }}` variable is used to include the mail layout CSS styles defined in the admin interface. The `{{ $subject }}` and `{!! $body !!}` variables are used to include the mail message subject and content, respectively.
+
+### Creating mail partials
+
+Mail partials are reusable components that can be included in mail templates and layouts. You can create mail partials by navigating to _Design > Mail templates > Partials_ in the admin interface. The `code` specified in the partial is a unique identifier and cannot be changed once created.
+
+Here is an example of a simple mail partial:
+
+```blade
+name = "Footer"
+==
+-------------------
+{{ $slot }}
+==
+
{{ $slot }}
+```
+
+You can include the mail partial in a mail template or layout using the `@partial` directive:
+
+```blade
+@partial('footer')
+
+@endpartial
+```
+
+### Mail variables
+
+You may access all of the data passed to the mail view or template by using the `{{ $variable }}` syntax. For example, if you pass a `$customer_name` variable to the mail view, you can access it in the view like this:
+
+```blade
+
Hello {{ $customer_name }},
+```
### Attachments
-### Inline attachments
+You can attach files to your emails using the `attach` method on the `Mail` facade. The `attach` method accepts the full path to the file as its first argument:
+
+```php
+use Illuminate\Support\Facades\Mail;
+
+Mail::send('vendor.extension::mail.message', $data, function($message) {
+ // ...
+ $message->attach('/path/to/file');
+});
+```
+
+#### Inline Attachments
+
+To embed media in your emails, you can use the `embed` method on the `$message` variable:
+
+```html
+
+
+
+```
+
+#### Embedding Raw Data
+
+To embed raw data in your emails, you can use the `embedData` method on the `$message` variable. This method accepts the raw data and the name of the file as its first and second arguments, respectively:
+
+```html
+
+
+
+```
## Sending mail
+To send a mail template in TastyIgniter, you can use the `sendTemplate` method on the `Mail` facade. This method accepts the code of the mail template, an array of data to pass to the view, and a closure that receives a message instance which allows you to customize the recipients, subject, and other aspects of the mail message:
+
+```php
+use Illuminate\Support\Facades\Mail;
+
+$data = [];
+
+Mail::sendTemplate('vendor.extension::mail.message', $data, function($message) use ($customer) {
+ $message->to($customer->email, $customer->name);
+});
+```
+
### Queueing mail
-## Registering mail layouts & partials
+To queue a mail message, you can use the `queueTemplate` method on the `Mail` facade. This method will automatically push the email onto the queue so it will be sent in the background by a queue worker. This can help to improve the response time of your application by offloading the sending of the email to a background process:
+
+```php
+use Illuminate\Support\Facades\Mail;
+
+$data = [];
+
+Mail::queueTemplate('vendor.extension::mail.message', $data, function($message) use ($customer) {
+ $message->to($customer->email, $customer->name);
+});
+```
+
+## Registering mail templates, layouts & partials
+
+To register mail templates, layouts, and partials in the Extension class, you can use the [`registerMailTemplates`](../extend/extensions#extension-class-methods), [`registerMailLayouts`](../extend/extensions#extension-class-methods), and ](../extend/extensions#extension-class-methods)[`registerMailPartials`] methods, respectively. These methods allow you to define the mail templates, layouts, and partials that your extension provides, making them available for customization via the admin interface:
+
+```php
+public function registerMailTemplates(): array
+{
+ return [
+ 'vendor.extension::mail.message' => 'Registered mail template message',
+ ];
+}
+
+public function registerMailLayouts(): array
+{
+ return [
+ 'vendor.extension::mail.layouts.default' => 'Default Layout',
+ ];
+}
+
+public function registerMailPartials(): array
+{
+ return [
+ 'vendor.extension::mail.partials.footer' => 'Footer partial',
+ ];
+}
+```
-## Registering mail templates
+## Mail local development
-## Mail Variables
+When developing locally, you may want to catch all outgoing mail messages and display them in the browser instead of sending them. You can use the `log` mail driver to log all outgoing mail messages to the log file. To enable this driver, set the `MAIL_DRIVER` environment variable to `log` in your `.env` file:
-## Mail & local development
\ No newline at end of file
+```bash
+MAIL_DRIVER=log
+```
diff --git a/advanced/routing.md b/advanced/routing.md
deleted file mode 100644
index 564211d..0000000
--- a/advanced/routing.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-title: "Routing"
-section: advanced
-sortOrder: 310
----
-
-More information on routing can be found in the Laravel Routing docs.
-
diff --git a/advanced/scheduling-tasks.md b/advanced/scheduling-tasks.md
index af5efb4..7aaf4f9 100644
--- a/advanced/scheduling-tasks.md
+++ b/advanced/scheduling-tasks.md
@@ -6,52 +6,75 @@ sortOrder: 330
## Introduction
-For each task you needed to schedule on your server, you may have generated a Cron entry in the past. This can quickly become a headache, because your schedule of tasks is no longer in source control and you must SSH into your server to add Cron entries.
+TastyIgniter's task scheduler is a powerful feature that allows you to define your command schedule within the application itself. It is built on top of the Laravel's task scheduling system, providing a fluent and expressive interface for defining your schedule.
-The command scheduler allows you to define your command schedule within the application itself fluently and expressively. Only one single Cron entry is required on your server when using the scheduler.
+When using the scheduler, only a single Cron entry is needed on your server. This eliminates the need for multiple Cron entries and allows you to keep your schedule of tasks in source control. For more detailed information, you can refer to the [Laravel Task Scheduling documentation](https://laravel.com/docs/scheduling).
-> **Note**: See the [installation guide](../installation) for instructions on how to set up the task scheduler.
+> **Note**: See the [installation guide](../installation#setting-up-the-task-scheduler) for instructions on how to set up the task scheduler.
-Task Scheduling is how scheduling time-based tasks are managed in TastyIgniter. Several core features of TastyIgniter, such as checking for updates, use the scheduler.
+Task Scheduling in TastyIgniter is the mechanism used to manage time-based tasks. Many essential features, such as update checks and auto-assignment of orders to staff, rely on the scheduler to function.
## Defining Schedules
-You may define all of your scheduled tasks by overriding the `registerSchedule` method within the [Extension registration class](../extend/extensions#registration). The method takes a single argument for `$schedule` and is used together with their frequency to define commands.
+To define scheduled tasks in TastyIgniter, you can override the `registerSchedule` method within the `Extension` class. This method takes a single argument `$schedule`, which is used to define commands along with their frequency.
-To get started, let's look at an example of how to schedule a task. In this example, we schedule to call a closure at midnight every day. We will execute a database query within the `Closure` to clear a table:
+Here's an example of how to schedule a task:
```php
-class Extension extends \System\Classes\BaseExtension
-{
- [...]
+// Inside your extension class
- public function registerSchedule($schedule)
- {
- $schedule->call(function () {
- \Db::table('recent_users')->delete();
- })->daily();
- }
+public function registerSchedule($schedule)
+{
+ $schedule->call(function () {
+ // Your task logic here
+ })->daily();
}
```
-In addition to scheduling `Closure` calls, you may also schedule console commands and operating system commands. For example, to schedule a console command, you can use the `command` method:
+In this example, a closure is scheduled to run at midnight every day. Inside the closure, you can define the logic for your task, such as executing a database query to clear a table.
+
+In addition to scheduling closure calls, you may also schedule [console commands](https://laravel.com/docs/artisan), [queued jobs](https://laravel.com/docs/queues) and operating system commands. For example, to schedule a console command, you can use the `command` method:
```php
$schedule->command('cache:clear')->daily();
```
+To schedule a queued job, you can use the `job` method:
+
+```php
+$schedule->job(new MyJob)->daily();
+```
+
The `exec` method may be used to issue a command to the operating system:
```php
$schedule->exec('node /home/acme/script.js')->daily();
```
-More information on task scheduling can be found on the Laravel Task Scheduling docs.
+More information on task scheduling can be found on the [Laravel Task Scheduling docs](https://laravel.com/docs/scheduling).
-## Creating commands
+## Writing commands
-TastyIgniter does not support generation of commands by `php artisan make:command`, instead use `php artisan create:command Vendor.Extension CommandName`. For example running `php artisan create:command Igniter.Cart ClearSessions` will create a command stub in the `extensions/igniter/cart/console` folder.
+To create a new command, you may use the `make:command` Artisan command.
-You should then register the command in your extension `register()` method to make it available to artisan, e.g. `$this->registerConsoleCommand('my.command', \Vendor\Extension\Console\MyCommand::class);`
+```bash
+php artisan create:command Vendor.Extension CommandName
+```
+
+This command will create a new command class in the `app/Console/Commands` directory. The generated command will include a `signature` and `description`, as well as a `handle` method where you may place your command's logic.
+```php
+namespace Vendor\Extension\Console;
+class CommandName extends \Illuminate\Console\Command
+{
+ protected string $signature = 'extension:command {argument : Description} {--option : Description}';
+
+ protected string $description = 'Command description';
+
+ public function handle()
+ {
+ // Your command logic here
+ }
+}
+```
diff --git a/advanced/testing-tastyigniter.md b/advanced/testing.md
similarity index 66%
rename from advanced/testing-tastyigniter.md
rename to advanced/testing.md
index 7cd53d4..f4456b4 100644
--- a/advanced/testing-tastyigniter.md
+++ b/advanced/testing.md
@@ -1,6 +1,6 @@
---
-title: "Testing TastyIgniter"
+title: "Testing"
section: advanced
-sortOrder: 340
+sortOrder: 390
callout: This section is incomplete. Please help to improve it.
---
diff --git a/advanced/validation.md b/advanced/validation.md
new file mode 100644
index 0000000..48652b4
--- /dev/null
+++ b/advanced/validation.md
@@ -0,0 +1,282 @@
+---
+title: "Validation"
+section: extend
+sortOrder: 370
+---
+
+## Introduction
+
+Validation is an essential part of TastyIgniter. It ensures that the data entered by users is accurate and meets the required criteria.
+
+## Defining validation rules
+
+Validation rules are defined as an array of key-value pairs, where the key is the field name, and the value is an array containing one or more validation rules.
+
+```php
+$rules = [
+ 'name' => ['required', 'min:5']
+ 'email' => ['required', 'email', 'unique:users']
+];
+```
+
+## Available validation rules
+
+TastyIgniter leverages Laravel's robust validation system, which provides a variety of validation rules that you can use to validate user input. For a comprehensive list of available validation rules and their usage, please refer to the [Laravel Validation Documentation](https://laravel.com/docs/validation#available-validation-rules).
+
+## Conditional validation
+
+You can conditionally apply [validation rules](../advanced/validation#available-validation-rules) based on the value of another field. To do this, you can use the `required_if`, `required_unless`, `required_with`, `required_with_all`, `required_without`, and `required_without_all` rules. For example, to require a field only if another field is present:
+
+```php
+$rules = [
+ 'password' => 'required_with:password_confirmation'
+];
+```
+
+## Validating arrays
+
+To validate an array of form fields, you can use the `.*` wildcard character to validate each element in the array. For example, to validate an array of email addresses:
+
+```php
+$rules = [
+ 'emails.*' => 'required|email'
+];
+```
+
+Or, you can validate each element in the array:
+
+```php
+$rules = [
+ 'users.*.email' => 'required|email'
+];
+```
+
+Or, to validate an array of form fields with a specific index:
+
+```php
+$rules = [
+ 'users.0.email' => 'required|email'
+];
+```
+
+Or, if the incoming HTTP request contains a `records[name]` field, you may define rules like so:
+
+```php
+$rules = [
+ 'records.name' => ['required', 'min:5']
+];
+```
+
+## Using the validator
+
+In typical scenarios within TastyIgniter, you should initially capture user input using the `post()` helper function. This input is then passed as the first argument to the `make` method, along with the validation rules as the second argument. Here’s how you can handle this in practice:
+
+```php
+$validator = Validator::make($data, [
+ 'name' => ['required', 'min:5'] // Array
+]);
+```
+
+To validate multiple fields, simply add each field and its corresponding rules to the validation array.
+
+```php
+$validator = Validator::make($data, [
+ 'name' => 'required',
+ 'password' => ['required', 'min:8'],
+ 'email' => ['required', 'email', 'unique:users']
+]);
+```
+
+### Checking the validation results
+
+Once you've created a Validator instance, you can use the `fails` (or `passes`) method to execute the validation checks.
+
+```php
+if ($validator->fails()) {
+ // The given data did not pass validation
+}
+```
+
+If validation has failed, you may retrieve the error messages from the validator.
+
+```php
+$messages = $validator->messages();
+```
+
+You can also access an array of the failed validation rules, without their accompanying messages, by using the `failed` method.
+
+```php
+$failed = $validator->failed();
+```
+
+### Throwing validation exceptions
+
+You can also handle validation errors by throwing Laravel's `\Illuminate\Validation\ValidationException`. This exception automatically returns the appropriate HTTP response based on the type of request.
+
+For a traditional HTTP request, it triggers a redirect response to the previous URL, along with the validation errors. If the request is an AJAX request, it instead returns a JSON response that includes the validation errors.
+
+```php
+$validator = Validator::make($data, $rules);
+
+if ($validator->fails()) {
+ throw new \Illuminate\Validation\ValidationException($validator);
+}
+```
+
+As a shorter way to validate the form similar to the example above, you can use the `validate` method directly.
+
+```php
+$data = Validator::validate($data, $rules);
+```
+
+### Customizing the error messages
+
+You can customize the error messages for each field by passing an array of custom messages as the third argument to the `make` method.
+
+```php
+$validator = Validator::make($data, $rules, [
+ 'name.required' => lang('author.extension::default.error_name')
+]);
+```
+
+### Customizing the validation attributes
+
+You can customize the field names used in the validation error messages by passing an array of custom attributes as the fourth argument to the `make` method.
+
+```php
+$validator = Validator::make($data, $rules, $messages, [
+ 'name' => lang('author.extension::default.label_name')
+]);
+```
+
+## Using the `Request` facade
+
+Another approach is to use the `Request` facade to validate all user inputs directly. This method simplifies the process by eliminating the need to manually supply the data; you only need to provide the validation rules as the first argument. The `validate` method then returns the filtered user data, including only the attributes and values that were successfully validated.
+
+```php
+$data = Request::validate([
+ 'name' => ['required', 'min:5'],
+ 'email' => ['required', 'email', 'unique:users']
+]);
+```
+
+In the example above, the `validate` method will return an array containing the validated data. If the validation fails, an exception will be thrown, and the user will be redirected back to the previous page with the validation errors.
+
+## Using form requests
+
+Form requests provide a convenient way to validate incoming HTTP requests in TastyIgniter. By defining validation rules and error messages in a single location, you can maintain and reuse the validation logic across multiple controller actions. This approach helps to keep your controller actions clean and readable by moving the validation logic out of the controller and into a separate class.
+
+Form Requests are typically stored in the extension's `src/Http/Requests` directory.
+
+### Creating a form request
+
+Form requests are custom request classes that extends the `Igniter\System\Classes\FormRequest` class, containing both validation and authorization logic. Here is an example of a simple form request class:
+
+```php
+namespace Author\Extension\Http\Requests;
+
+class RecordRequest extends \Igniter\System\Classes\FormRequest
+{
+ public function rules(): array
+ {
+ return [
+ 'name' => ['required', 'min:5'],
+ 'email' => ['required', 'email', 'unique:users']
+ ];
+ }
+}
+```
+
+### Applying the form request
+
+To use the form request in your admin controller to validate form fields. You can add the form request class to the controller's `$formConfig` property along with the `FormController` action class.
+
+```php
+namespace Author\Extension\Http\Controllers;
+
+class MyController extends \Igniter\Admin\Controllers\Controller
+{
+ public array $implement = [\Igniter\Admin\Http\Actions\FormController::class];
+
+ public $formConfig = [
+ 'request' => \Author\Extension\Http\Requests\RecordRequest::class,
+ 'create' => [
+ // ...
+ ],
+ ];
+}
+```
+
+### Performing additional validation
+
+After the initial validation, you may need to perform further validation. You can do this by calling the form request's `after` function.
+
+The `after` function should return an array of callables or closures that will be executed after validation is complete. The specified callables will receive an `Illuminate\Validation\Validator` object, allowing you to issue extra error messages as needed:
+
+```php
+namespace Author\Extension\Http\Requests;
+
+use Illuminate\Validation\Validator;
+
+class RecordRequest extends \Igniter\System\Classes\FormRequest
+{
+ public function after(Validator $validator): array
+ {
+ return [
+ new \Author\Extension\Validation\ValidateUserStatus,
+ function ($validator) {
+ if ($this->somethingElseIsInvalid()) {
+ $validator->errors()->add('field', 'Something is wrong with this field!');
+ }
+ },
+ ];
+ }
+}
+```
+
+### Stopping on the first validation failure
+
+By default, the form request will continue to validate all fields, even if one field fails validation. If you want to stop validation on the first failure, you can set the `stopOnFirstFailure` property to `true` in the form request class.
+
+```php
+protected $stopOnFirstFailure = true;
+```
+
+### Customizing the redirect location
+
+By default, if validation fails, the user is redirected back to the previous page. However, you can customize this behavior in your form request class. To specify a different redirect location, you can override the `$redirect` property. Alternatively, if you prefer to redirect users to a specific named route, you can set the `$redirectRoute` property like so:
+
+```php
+protected $redirect = '/custom-url'; // Redirects to a custom URL
+
+// Or
+
+protected $redirectRoute = 'route-name'; // Redirects to a named route
+```
+
+### Customizing the error messages
+
+You can customize the error messages for each field by overriding the `messages` method in the form request class.
+
+```php
+public function messages(): array
+{
+ return [
+ 'name.required' => lang('author.extension::default.error_name')
+ ];
+}
+```
+
+### Customizing the validation attributes
+
+You can customize the field names used in the validation error messages by overriding the `attributes` method in the form request class.
+
+```php
+public function attributes(): array
+{
+ return [
+ 'name' => lang('author.extension::default.label_name')
+ ];
+}
+```
+
diff --git a/customize/components.md b/customize/components.md
index 47f957d..2dbfa1c 100644
--- a/customize/components.md
+++ b/customize/components.md
@@ -1,82 +1,573 @@
---
title: "Components"
-section: customize
+section: extend
sortOrder: 140
---
## Introduction
-TastyIgniter Components implements content and features that extend your website. They can be added, removed, and rearranged from **Design > Themes > Editor** in the Administration Panel.
+Components implements content and features that extend your TastyIgniter website. They can be added, removed, and rearranged from **Design > Themes > Editor** in the Administration Panel.
-Other than displaying HTML markup on a page, components can implement the handling of [AJAX requests](../advanced/ajax-request), the handling of postbacks and the handling of the page execution life cycle, which enables pages to be injected.
+Other than displaying HTML markup on a page, components can implement the handling of [AJAX requests](../advanced/ajax-request).
-This article describes using components within themes and does not explain [building components](../extend/building-components) as part of extensions.
+In this section, you'll learn the basics of building, registering and rendering both [Igniter](../customize/components#theme-component) and [Livewire](#livewire-component) components within a TastyIgniter application. Starting with version 4, Livewire components are not fully supported. You can register these using the `registerComponents` method and attach them into your site via the Admin Interface, just like the Theme components.
-## Attaching components
+## Livewire Component
-Use the admin interface to attach components to pages and layouts. You can attach a component to a page or layout manually using a file editor by following the example below:
+To create a Livewire component, extend the `\Livewire\Component` class, implement the `DefineComponent` interface, and register it to be displayed on your site and available from the Admin Interface. If you're new to Livewire component development, the [Components section of the Livewire documentation](https://livewire.laravel.com/docs/components) provides a good starting point.
+
+Livewire components are stored in the `/src/Livewire` subdirectory within an extension directory. Additionally, component partials should be placed in `resources/views/livewire` to align with Laravel package conventions.
```yaml
----
-title: My first page
-permalink: "/page"
+vendor/
+ acme/
+ helloworld/ <=== Extension directory
+ src/ <=== Extension source directory
+ Livewire/ <=== Livewire component subdirectory
+ HelloBlock.php <=== Component class file
+ resources/ <=== Extension resources directory
+ views/ <=== Extension views directory
+ livewire/ <=== Livewire blade views subdirectory
+ hello-block.blade.php <=== Component default blade view (optional)
+```
-'[cartBox]':
- showCartItemThumb: 1
----
+### Defining the component
+
+To begin, you can either use your preferred file manager to create files and directory for the **HelloBlock** component.
+
+The component class file should extend the `\Livewire\Component` base class, implement `\Igniter\System\Contracts\SupportsLivewireComponent` interface and its methods, and define the properties of the component. The following is an example defines the `HelloBlock` component:
+
+```php
+namespace Acme\HelloWorld\Livewire;
+
+class HelloBlock extends \Livewire\Component implements \Igniter\System\Contracts\SupportsLivewireComponent
+{
+ public int $maxItems = 5;
+
+ public function alerts(): array
+ {
+ return ['Success Alert', 'Warning Alert', 'Danger Alert'];
+ }
+
+ public function render()
+ {
+ return view('igniter.helloworld::livewire.default');
+ }
+}
```
-The **cartBox** component in the above example initializes the property **showCartItemThumb** with value **1**.
+The component properties and methods will automatically be made available to the component's view. For example, you will be able to access its `alerts` method and `$maxItems` property from the `resources/views/livewire/hello-block.blade.php` blade view. For example:
-When you attach a component, a page variable that matches the component name is automatically created (`$cartBox` in the previous example). Render components HTML markup on a page or layout as follows:
+```blade
+
Max alerts: {{ $maxItems }}
+@foreach ($this->alerts() as $message)
+
{{ $message }}
+@endforeach
+```
+
+### Component registration
+
+Components must be registered by overriding the `registerComponents` method within the [Extension registration class](../extend/extensions#extension-class). This lets the app know about the component and so it can be attached through the Admin Interface. To register the `HelloBlock` component with the default alias name **hello-block**, you may override the `registerComponents` method:
+
+```php
+public function registerComponents(): array
+{
+ return [
+ \Acme\HelloWorld\Livewire\HelloBlock::class => [
+ 'code' => 'hello-block',
+ 'name' => 'Name of the hello block component',
+ 'description' => 'Description of the hello block component',
+ ],
+ ];
+}
+```
+
+### Component properties
+
+Components can be configured using properties defined for each component using class properties. Let's define a **maxItems** property to limit the number of alerts allowed
+
+If you want to manage your component's properties through the Admin Interface, you can use `#[DefineProperty]` attribute.
+
+```php
+namespace Acme\HelloWorld\Livewire;
+
+use Igniter\System\Attributes\DefineProperty;
+
+class HelloBlock extends \Livewire\Component implements \Igniter\System\Contracts\SupportsLivewireComponent
+{
+ #[DefineProperty(label: 'Max items', type: 'number', default: 5)]
+ public int $maxItems = 5;
+
+ // ...
+}
+```
+| Key | Description |
+| --------------- | ------------------------------------------------------------ |
+| **label** | required, the property label, it is used by the component Selector in the Admin Interface. |
+| **type** | optional, specifies the property type. The type defines the form field type. Currently supported types are ** text**, **number**, **checkbox**, **radio**, **select** and **selectlist**. Default value: **text**. |
+| **default** | optional, the default property value. |
+| **comment** | optional, the property description, it is used by the component Selector in the Admin Interface. |
+| **placeholder** | optional placeholder for text and select properties. |
+| **options** | optional array of options for checkbox, radio, select, selectlist properties. |
+
+The **options** property key can be static or dynamic. Using the `maxItems` property, let's define static options:
+
+```php
+namespace Acme\HelloWorld\Livewire;
+
+class HelloBlock extends \Livewire\Component implements \Igniter\System\Contracts\SupportsLivewireComponent
+{
+ #[DefineProperty(label: 'Max items', type: 'select', options: [
+ 20 => '20 Per page',
+ 50 => '50 Per page'
+ ])]
+ public int $maxItems = 20;
+
+ // ...
+}
+```
+
+Dynamically, the options can be fetched by omitting the `options` key from the property definition and defining a method that returns the list of options. The method name should be the studly cased name of the property. For example, `getPropertyOptions` where **Property** is the name of the property. In this example, we'll define a method for the `maxItems` property.
+
+```php
+namespace Acme\HelloWorld\Livewire;
+
+class HelloBlock extends \Livewire\Component implements \Igniter\System\Contracts\SupportsLivewireComponent
+{
+ #[DefineProperty(label: 'Max items', type: 'select')]
+ public int $maxItems = 20;
+
+ public function getMaxItemsOptions(): array
+ {
+ return [
+ 20 => '20 Per page',
+ 50 => '50 Per page'
+ ];
+ }
+}
+```
+
+Reading the property value from within the **component class**:
+
+```php
+$maxItems = $this->maxItems;
+```
+
+Accessing the property value from a **component partial**:
+
+```php
+{{ $maxItems }}
+```
+
+For more information, reference the [Livewire Component Properties documentation](https://livewire.laravel.com/docs/properties).
+
+### Component actions
+
+Livewire actions are defined as class methods prefixed with `on` followed by the event name. These methods can be triggered by frontend activities such as clicking a button or filling out a form using the `wire:click` or `wire:submit` directives. For example, to handle an AJAX request with the event name `onAddItem`, define a method in the component class as follows:
+
+```php
+public function onAddItem()
+{
+ $value1 = post('value1');
+ $value2 = post('value2');
+ $this->page['result'] = $value1 + $value2;
+}
+```
+
+The action can be triggered from the blade view as follows:
```blade
-@component('cartBox')
+
```
-### Using multiple instances of the same component
+For more information, reference the [Livewire Component Actions documentation](https://livewire.laravel.com/docs/actions).
-If two components with the same name are assigned to a page and layout together, the page component overrides any properties of the layout component.
+### Component lifecycle handlers
+
+Livewire includes a number of lifecycle hooks that allow you to run code at specific stages of a component's lifecycle. These hooks let you to conduct activities before or after specific events, including as component initialization, property updates, and template rendering.
-You can attach components of the same name registered within two extensions by using its fully qualified class name and assigning it an *alias*:
+| Hook Method | Description |
+|------------------| ------------------------------------------------------------ |
+| **mount()** | Called when a component is created |
+| **hydrate** | Called when a component is re-hydrated at the beginning of a subsequent request |
+| **boot** | Called at the beginning of every request. Both initial, and subsequent |
+| **updating** | Called before updating a component property |
+| **updated** | Called after updating a property |
+| **rendering** | Called before render() is called |
+| **rendered** | Called after render() is called |
+| **dehydrate** | Called at the end of every component request |
+
+For more information, reference the [Livewire Component Livecycle Hooks documentation](https://livewire.laravel.com/docs/lifecycle-hooks).
+
+### Rendering the component
+
+Use the admin interface to display components on pages and layouts. You can also display a component on a page or layout manually using a file editor by following the example below:
```blade
-'[Igniter\Cart\Components\CartBox cartBox]':
- showCartItemThumb: 1
+
+```
-'[Igniter\Local\Components\CartBox localCartBox]':
- showCartItemThumb: 1
----
-@component('cartBox')
+The **acme.hello-world** prefix in the above example presents the code of the extension that registers the component.
+
+You can pass variables to components by defining them as attributes on the component tag. For example:
-@component('localCartBox')
+```blade
+
+```
+
+If you need to pass dynamic values or variables to a component, you can write PHP expressions in component attributes by prefixing the attribute with a colon:
+
+```blade
+
```
-Define multiple instances of a same component:
+You can access variables within the component like any other markup variable:
```blade
-'[cartBox cartBoxA]':
- showCartItemThumb: 1
+
+ @foreach($pages as $page)
+
{{ $page->name }}
+ @endforeach
+
+```
+
+For more information, reference the [Rendering components section of the Livewire documentation](https://livewire.laravel.com/docs/components#rendering-components).
+
+### Inject page assets
+
+Use the `Assets::addCss` and `Assets::addJs` methods to add assets to the pages the components are attached to:
+
+```php
+public function mount()
+{
+ Assets::addJs('acme.helloworld::/js/block.js', 'helloworld-block');
+ Assets::addCss('acme.helloworld::/js/block.css', 'helloworld-block');
+}
+```
+
+## Theme Component
+
+To create a theme component, extend the `BaseComponent` class, implement its methods, and register it to be displayed on your site and available from the Admin Interface.
+
+Theme component classes live in the **/src/Components** subdirectory of an extension directory, while the component partials live in the **/resources/views/_components** subdirectory.
+
+```yaml
+vendor/
+ acme/
+ helloworld/ <=== Extension directory
+ src/ <=== Extension source directory
+ Components/ <=== Components subdirectory
+ HelloBlock.php <=== Component class file
+ resources/ <=== Extension resources directory
+ views/ <=== Extension views directory
+ _components/ <=== Components subdirectory
+ helloBlock/ <=== Component subdirectory
+ default.blade.php <=== Component default partial (optional)
+```
+
+### Defining the component
+
+To begin, you can either use your preferred file manager to create files and directory for the **HelloBlock** component or simply call the following command from the application directory to generate a component with basic files and directories:
+
+```bash
+php artisan make:igniter-component Acme.HelloWorld HelloBlock
+```
+
+The component class file should extend the `\Igniter\System\Classes\BaseExtension` base class and define the functionality and properties of the component. The following is an example defines the `HelloBlock` component:
+
+```php
+namespace Acme\HelloWorld\Components;
+
+class HelloBlock extends \Igniter\System\Classes\BaseComponent
+{
+ public function defineProperties(): array
+ {
+ return [];
+ }
+
+ public function alerts(): array
+ {
+ return ['Success Alert', 'Warning Alert', 'Danger Alert'];
+ }
+}
+```
+
+The component properties and methods are available on the page or layout its attached through the component variable
+which corresponds to the alias of the component. For example, if the `HelloBlock` component was defined on a page or
+layout as `'[helloBlock]'`, you will be able to access its `alerts` through the `$helloBlock` variable:
+
+```blade
+@foreach ($helloBlock->alerts() as $message)
+
{{ $message }}
+@endforeach
+```
+
+### Component registration
+
+Components must be registered by overriding the `registerComponents` method within the [Extension registration class](../extend/extensions#extension-class). This lets the app know about the component and gives it an **alias name** for its use within themes. To register the `HelloBlock` component with the default alias name **helloBlock**, you may override the `registerComponents` method:
+
+```php
+public function registerComponents(): array
+{
+ return [
+ \Acme\HelloWorld\Components\HelloBlock::class => [
+ 'code' => 'helloBlock',
+ 'name' => 'Name of the hello block component',
+ 'description' => 'Description of the hello block component',
+ ],
+ ];
+}
+```
+
+### Component properties
+
+Components can be configured using properties defined for each component by overriding the `defineProperties` method. Let's define a **maxItems** property to limit the number of alerts allowed
-'[cartBox cartBoxB]':
- showCartItemThumb: 1
+```php
+public function defineProperties(): array
+{
+ return [
+ 'maxItems' => [
+ 'title' => 'Max items',
+ 'description' => 'The most number of alert items allowed',
+ 'default' => 10,
+ 'type' => 'text',
+ ]
+ ];
+}
+```
+
+The method should return an array with the property keys as indexes and the property parameters as values. The property keys are used to access the component property values within the component class.
+
+| Key | Description |
+| --------------- | ------------------------------------------------------------ |
+| **label** | required, the property label, it is used by the component Selector in the Admin Interface. |
+| **type** | optional, specifies the property type. The type defines the form field type. Currently supported types are ** text**, **number**, **checkbox**, **radio**, **select** and **selectlist**. Default value: **text**. |
+| **default** | optional, the default property value. |
+| **comment** | optional, the property description, it is used by the component Selector in the Admin Interface. |
+| **placeholder** | optional placeholder for text and select properties. |
+| **options** | optional array of options for checkbox, radio, select, selectlist properties. |
+
+
+The **options** property key can be static or dynamic. Using the `maxItems` property, let's define static options:
+
+```php
+public function defineProperties(): array
+{
+ return [
+ 'maxItems' => [
+ ...
+ 'type' => 'select',
+ 'options' => [
+ '20' => '20 Per page',
+ '50' => '50 Per page'
+ ]
+ ]
+ ];
+}
+```
+
+Dynamically, the options can be fetched by omitting the `options` key from the property definition and defining a method that returns the list of options. The method name should be the studly cased name of the property. For example, `getPropertyOptions` where **Property** is the name of the property. In this example, we'll define a method for the `maxItems` property.
+
+```php
+public function defineProperties(): array
+{
+ return [
+ 'maxItems' => [
+ ...
+ 'type' => 'select',
+ ]
+ ];
+}
+
+public function getMaxItemsOptions(): array
+{
+ return [
+ '20' => '20 Per page',
+ '50' => '50 Per page'
+ ];
+}
+```
+
+Reading the property value from within the **component class**:
+
+```php
+// Get a single value
+$maxItems = $this->property('maxItems');
+
+// Get a value and return a default value if it doesn't exist
+$maxItems = $this->property('maxItems', 8);
+
+// Get all properties as array
+$properties = $this->getProperties();
+```
+
+Accessing the property value from a **component partial**:
+
+```php
+{{ $__SELF__->property('maxItems') }}
+```
+
+### Component class methods
+
+Sometimes you might want to handle some initialization logic before AJAX handlers and before the execution cycle of the page. You can override the `initialize` method in the component class to handle such initialization logic.
+
+The following methods are supported in the component class:
+
+| Method | Description |
+|---------------------|------------------------------------------------------------------------------------------|
+| **initialize()** | initialization method, called when the component is attached to a page or layout |
+| **onRun()** | override to hook into the page's execution life cycle. |
+| **onRender()** | override to handle specific logic before rendering the default partial of the component. |
+| **renderPartial()** | render a specified component partial or shared partial. |
+
+### Component partials
+
+Besides the default partial, components can also provide additional partials which can be used within other template files or in the default partial itself.
+
+Components can share partial files by placing them in the **resources/views/_partials** subdirectory of the extension directory.
+
+```yaml
+vendor/
+ acme/
+ helloworld/
+ resources/
+ views/
+ _components/ <=== Components directory
+ helloBlock/ <=== Component partials directory
+ default.blade.php <=== Component default partial file
+ _partials/ <=== Component shared partials directory
+ shared.blade.php <=== Component shared partial file
+```
+
+Blade's `@themePartial` directive allows you to include the **helloBlock** component partial from within another component partial:
+
+```blade
+@themePartial('helloBlock::shared')
+```
+
+Partials rendered from outside the component must use their fully qualified name.
+
+```blade
+@themePartial('componentName::component-partial')
+```
+
+**Overriding component partials**
+
+All component partials can be overridden by creating theme partials. A component defined with alias **helloBlock** can have its **default.blade.php** partial overridden by creating a theme partial called **_partials/helloBlock/default.blade.php**
+
+### Component lifecycle handlers
+
+Every time the page loads, the `MainController` executes the `onRun` method of the attached components. Overriding the method in the component class allows components to hook into the page execution cycle. Let's inject some variables into the page:
+
+```php
+public function onRun()
+{
+ $this->page['var'] = 'value';
+}
+```
+
+Handler functions can be defined in the layout and page [PHP code section](../customize/themes#php-code-section) and component classes. Here is an example of a page or layout PHP section with handler functions:
+
+```blade
+---
+public function onStart()
+{
+ //
+}
+---
+
HTML markup
+```
+
+These handlers are executed in the following sequence:
+
+- `onInit` layout function
+- `onInit` page function
+- `onStart` layout function
+- `onRun` layout components function
+- `onStart` page function
+- `onRun` page components function
+- `onEnd` page function
+- `onEnd` layout function
+
+### Defining AJAX handlers
+
+Components class can define AJAX event handlers as methods prefixed with `on` followed by the event name. For example, to handle an AJAX request with the event name `onAddItem`, define a method in the component class as follows:
+
+```php
+public function onAddItem()
+{
+ $value1 = post('value1');
+ $value2 = post('value2');
+ $this->page['result'] = $value1 + $value2;
+}
+```
+
+If the alias for this component was `helloBlock` this handler can be accessed by `helloBlock::onAddItem`.
+
+> Please see the [Handling AJAX Requests](../advanced/ajax-request) article for more details.
+
+### Rendering the component
+
+Use the admin interface to attach components to pages and layouts. You can also attach a component to a page or layout manually using a file editor by following the example below:
+
+```yaml
---
-@component('cartBoxA')
+title: My first page
+permalink: "/page"
+
+'[helloBlock]':
+ maxItems: 20
+---
+```
+
+The **helloBlock** component in the above example initializes the property **maxItems** with value **20**.
+
+When you attach a component, a page variable that matches the component name is automatically created (`$helloBlock` in the previous example).
+
+Render components HTML markup on a page or layout as follows:
+
+```blade
+@themeComponent('helloBlock')
+```
+
+**Component variables**
+
+The `@themeComponent('helloBlock')` tag accepts an array of variables as its second parameter. The specified variables will be available at the time of rendering and will explicitly override the component property values:
-@component('cartBoxB')
+```blade
+@themeComponent('helloBlock', ['maxItems' => 100])
```
-## Component variables
+**Rendering multiple instances of the same component**
+
+If two components with the same name are assigned to a page and layout together, the page component overrides any properties of the layout component.
-The `@component('cartBox')` tag accepts an array of variables as its second parameter. The specifed variables will be available at the time of rendering and will explicitly override the component property values:
+You can render multiple instances of the same components by assigning it an *alias*:
```blade
-@component('cartBox', ['showCartItemThumb' => 0])
+'[helloBlock helloBlockA]':
+ maxItems: 1
+
+'[helloBlock helloBlockB]':
+ maxItems: 5
+---
+@themeComponent('helloBlockA')
+
+@themeComponent('helloBlockB')
```
-The **showCartItemThumb** property value of the component will be set to **0** when the component is being rendered.
+### Inject page assets
-## Overriding component partials
+Use the `addCss` and `addJs` controller methods to add assets to the pages or layouts the components are attached to:
-All component partials can be overridden by creating theme partials. A component defined with alias **cartBox** can have its **control.blade.php** partial overridden by creating a theme file called **_partials/cartBox/control.blade.php**
+```php
+public function onRun()
+{
+ // Assets path that begins with a slash (/) is relative to the website root directory
+ $this->addJs('acme.helloworld::/js/block.js', 'helloworld-block);
+ // Paths that does not begin with a slash is relative to the component directory
+ $this->addJs('js/block.js', 'helloworld-block);
+}
+```
diff --git a/customize/layouts.md b/customize/layouts.md
index f7ffbe9..ad35a50 100644
--- a/customize/layouts.md
+++ b/customize/layouts.md
@@ -6,18 +6,20 @@ sortOrder: 120
## Introduction
-Layouts are templates that wrap around your content. They allow you to have your template source code in one place so that you don't have to repeat things like your header and footer on every page.
+In TastyIgniter, a theme layout is a template that wraps around your content. It allows you to have your template source code in one place so that you don't have to repeat things like your header and footer on every page.
-Layout files live in the **/_layouts** subdirectory of a theme directory.
+Layout files live in the **resources/views/_layouts** subdirectory of a theme directory. For example:
```yaml
-themes/
- your-theme/ <=== Theme directory
- _layouts/ <=== Layouts subdirectory
- default.blade.php <=== Layout template file
+acme/ <=== Theme vendor directory
+ purple/ <=== Theme directory
+ resources/
+ views/
+ _layouts/ <=== Layouts subdirectory
+ default.blade.php <=== Layout template file
```
-The convention is to have a basic layout called `default.blade.php` and be used by other pages as required. Within the layout file, you should use the `@page` tag to display the content of the page.
+The convention is to have a basic layout called `default.blade.php` and be used by other pages as required. Within the layout file, you should use the `@themePage` tag to display the content of the page.
```blade
@@ -26,28 +28,23 @@ The convention is to have a basic layout called `default.blade.php` and be used
- @page
+ @themePage