diff --git a/.vitepress/config/sidebar.ts b/.vitepress/config/sidebar.ts index 2b77a582..f59fdd64 100644 --- a/.vitepress/config/sidebar.ts +++ b/.vitepress/config/sidebar.ts @@ -155,7 +155,8 @@ const sidebar = [ // { text: 'Views', link: '/docs/frontend/mvc' }, { text: 'Models', link: '/docs/database/models' }, { text: 'Migrations', link: '/docs/database/migrations' }, - { text: 'Schema', link: '/docs/database/schema' }, + { text: 'JSON Schema', link: '/docs/database/schema' }, + // { text: 'Schema Files', link: '/docs/database/files' }, { text: 'Seeders', link: '/docs/database/seeders' }, { text: 'Factories', link: '/docs/database/factories' }, { text: 'Writing Commands', link: '/docs/mvc/commands' }, diff --git a/.vitepress/future/files.md b/.vitepress/future/files.md index 9bcc6f66..ea85d448 100644 --- a/.vitepress/future/files.md +++ b/.vitepress/future/files.md @@ -1,13 +1,13 @@ -# Database Files +# Schema Files -Leaf MVC inherited all the teachings of Laravel and Ruby on Rails, including the use of migrations, seeders, and factories which made creating, testing and seeding databases a breeze. On top of that, Leaf MVC also introduced a new concept called schema files which allowed you to generate migrations from JSON data. While this was a great feature, it was a bit too much for a lot of developers and added to the growing hell of files in your app. To solve this, we've decided to move away from the Rails/Laravel way of doing things and introduce a new way of handling database files in Leaf MVC. +Leaf MVC inherited all the teachings of Laravel and Ruby on Rails, including the use of migrations, seeders, and factories which made creating, testing and seeding databases a breeze. It even introduced schema files, allowing you to generate migrations from JSON data. While helpful, this added complexity and clutter to projects. To simplify things, we’re moving away from the Rails/Laravel approach and creating a more streamlined, Leaf-like solution. -## What are Database Files? +## What are Schema Files? -Database files are a way to handle migrations, seeders, and factories in a single file. This way, you can easily manage your database structure without having to create multiple files for each operation and without having to repeat yourself all over your app. Database files are written in yaml which makes them incredibly easy to read and write. +Schema files build on the JSON schema idea we introduced in earlier Leaf MVC versions, but they take things further. Instead of juggling separate files for migrations, seeders, and factories, you can handle everything in one place. They’re written in YAML, so they’re easy to read and work with—no extra hassle, no repeating yourself. ```yml [flights.yml] -defaultId: true +increments: true timestamps: true columns: to: string @@ -17,23 +17,29 @@ columns: seeds: count: 10 data: - to: faker.city - from: faker.city - identifier: faker.uuid + to: 'faker:city' + from: 'faker:city' + identifier: 'faker:uuid' ``` -## Creating a Database File +## Creating a Schema File -Aloe comes with a `g:db` command that you can use to generate a database file. You can generate a database file by running: +Aloe comes with a `g:schema` command that you can use to generate a database file. You can generate a database file by running: ```bash:no-line-numbers -php leaf g:db users +php leaf g:schema ``` -This will create a database file at `app/database/users.yml` which looks like this: +Remember, every schema file is tied to a table in your database. When you run the command above, Leaf will create a schema file in your `app/database` directory with the name `.yml`. Here’s an example: + +```bash:no-line-numbers +php leaf g:schema users +``` + +This will create a schema file at `app/database/users.yml` which looks like this: ```yml [users.yml] -defaultId: true +increments: true timestamps: true columns: name: string @@ -47,14 +53,14 @@ columns: seeds: count: 10 data: - name: faker.name - email: faker.email - password: "{{ bcrypt('password') }}" + name: 'faker:name' + email: 'faker:email' + password: 'hash:password' ``` Breaking down this file, we have: -- `defaultId`: This is used to set the default id of the table. If set to `true`, the table will have an auto-incrementing id. If set to `false`, the table will not have an id. +- `increments`: This is used to set the default id of the table. If set to `true`, the table will have an auto-incrementing id. If set to `false`, the table will not have an id, and you can set your own primary key. - `timestamps`: This is used to set timestamps on the table. If set to `true`, the table will have `created_at` and `updated_at` columns. If set to `false`, the table will not have timestamps. @@ -74,12 +80,12 @@ Breaking down this file, we have: - `seeds`: This is used to set the seeders of the table. The available properties are: - `count`: This is used to set the number of seeds to generate. - - `data`: This is used to set the data of the seeds. The key is the column name and the value is the value of the column. You can use `faker.[value]` to generate fake data for the column. You can also use `{{ [value] }}` to use PHP code. + - `data`: This is used to set the data of the seeds. The key is the column name and the value is the value of the column. You can use `faker:[value]` to generate fake data for the column. - `truncate`: This is used to truncate the table before seeding. -## DB Migrations +## Database tables -Traditionally, migrations are used to create database tables and modify them. In Leaf MVC, you can create migrations in your database files. The `columns` key in your database file is used to create migrations. Here's an example of a migration: +Traditionally, migrations are used to create database tables and modify them. In Leaf MVC, every schema file is tied to a particular table which is the name of the file. All you need to do is modify the columns of the table using the `columns` key in your schema file. Here's an example: ```yml [users.yml] columns: @@ -92,27 +98,69 @@ columns: email_verified_at: timestamp ``` -In this example, we create a migration that creates a `users` table with `name`, `email`, `password`, and `email_verified_at` columns. To run your migrations, you can use the `db:migrate` command: +In this example, we create a `users` table with `name`, `email`, `password`, and `email_verified_at` columns. We can then migrate this table to our database using the `db:migrate` command: ```bash:no-line-numbers php leaf db:migrate ``` - +Now you just need to run the script using the `db:script` command: + +```bash:no-line-numbers +php leaf db:script ImportUsersFromOldTable +``` --> + +## Migration histories + +Migration histories are used to keep track of the migrations that have been run on your database. This is useful for keeping track of the state of your database so you can easily roll back to a previous state if needed. Unlike in other frameworks, Leaf MVC does require you to manually create migrations to keep track of your migration history. This is done automatically for you. + +Every time you edit a schema file and run the `db:migrate` command, Leaf will automatically keep track of the migrations that have been run on your database, which means less time scrambling through migration files and more time building your app. + +In the end, this means you can continue to use `php leaf db:rollback` to roll back your database to a previous state. -## DB Seeders +## Seeding your database + +Database seeds are a way to populate a database with initial data. This initial data can be used to set up default values or pre-populate a database with test data. Database seeds typically contain small amounts of data, such as default settings, test data, or sample records. Seeders are used to populate your database with dummy data. In Leaf MVC, you can create seeders in your database files. The `seeders` key in your database file is used to create seeders. Here's an example of a seeder: @@ -121,20 +169,23 @@ seeds: data: - name: 'Example User' email: 'example@example.com' - password: "{{ bcrypt('password') }}" + password: 'hash:password' + - name: 'Another User' + email: 'another@example.com' + password: 'hash:password' ``` -In this example, we create a seeder that seeds the `users` table with an example user. We are passing an array of seeds to the `data` key, each seed being a key value pair of column name and value. +In this example, we create a seeder that seeds the `users` table with two example users. We are passing an array of seeds to the `data` key, each seed being a key value pair of column name and value. -If you want to generate multiple seeds, you can pass an object to the `data` key instead of an array together with a `count` key: +Another way to generate multiple seeds is to use the `count` key. When using the `count` key, you can pass an integer value to generate multiple seeds with the same data. Here's an example: -```yml [users.yml] +```yml{2} [users.yml] seeds: count: 10 data: name: 'Example User' email: 'example@example.com' - password: "{{ bcrypt('password') }}" + password: 'hash:password' ``` After creating your seeder, you can run your seeders using the `db:seed` command: @@ -145,15 +196,27 @@ php leaf db:seed This will generate 10 seeds for the `users` table with the same data which is not very useful. To generate multiple fake seeds, you can use what other frameworks call a factory. -In Leaf MVC, factories and seeders are the same thing as we believe this confusion is unnecessary. If you want to generate fake data for your seeders, you can add `faker.[value]` as the value of a column in your seeder. Here's an example: +In Leaf MVC, factories and seeders are the same thing as we believe this confusion is unnecessary. If you want to generate fake data for your seeders, you can add `faker:[value]` as the value of a column in your seeder. Here's an example: ```yml{4,5} [users.yml] seeds: count: 10 data: - name: faker.name - email: faker.email - password: "{{ bcrypt('password') }}" + name: 'faker:name' + email: 'faker:email' + password: 'hash:password' ``` In this example, we're generating 10 fake seeds for the `users` table. + +After adding your seeds, you can run your seeders using the `db:seed` command: + +```bash:no-line-numbers +php leaf db:seed +``` + +If you want to seed a specific table, you can pass the table name as an argument to the `db:seed` command: + +```bash:no-line-numbers +php leaf db:seed users +``` diff --git a/src/docs/auth/index.md b/src/docs/auth/index.md index 2b39921b..5860687f 100644 --- a/src/docs/auth/index.md +++ b/src/docs/auth/index.md @@ -32,9 +32,11 @@ The next step is to link your database and start signing users in. To do any kind of authentication, you need to connect to some kind of database which will store your users' data. If you are already using Leaf DB or Leaf MVC, then your database connection will automatically be used by Leaf Auth, so you don't need to connect to your database again. -If you are not using Leaf DB or Leaf MVC, you can connect to your database manually: +If you are **NOT** using Leaf DB or Leaf MVC, you can connect to your database manually: -```php +::: code-group + +```php [Auth connect] auth()->connect([ 'dbtype' => '...', 'charset' => '...', @@ -46,9 +48,7 @@ auth()->connect([ ]); ``` -If you have an existing PDO connection, you can pass it to Leaf Auth: - -```php +```php [Existing PDO instance] $db = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', ''); auth()->dbConnection($db); @@ -56,6 +56,8 @@ auth()->dbConnection($db); // you can use leaf auth the same way you always have ``` +::: + ## Auth + Leaf MVC If you are using Leaf MVC, you can set up Leaf Auth to work with your default database connection by heading over to the `public/index.php` file and uncommenting the line that connects to the database: @@ -90,12 +92,26 @@ Leaf Auth doesn't give you any structure for your database, with that, you can s 1. By default, Leaf Auth assumes that your database primary key is `id`. If you have a database where you are using another field, say `admin_id` as the primary key, you will need to tell Leaf the name of your primary key. You can do this using the `id.key` config: - ```php:no-line-numbers + ::: code-group + + ```php:no-line-numbers [Leaf] auth()->config('id.key', 'admin_id'); ``` + ```php:no-line-numbers [Leaf MVC - config/auth.php] + 'id.key' => 'admin_id' + ``` + 2. Leaf Auth assumes that you will save your users in a database table named `users`, this might however not be the case for your application. If you want to use a different table, you can configure Leaf Auth using `db.table`: - ```php:no-line-numbers + ::: code-group + + ```php:no-line-numbers [Leaf] auth()->config('db.table', 'admins'); ``` + + ```php:no-line-numbers [Leaf MVC - config/auth.php] + 'db.table' => 'admins' + ``` + + ::: diff --git a/src/docs/auth/login.md b/src/docs/auth/login.md index 29712818..485f1b25 100644 --- a/src/docs/auth/login.md +++ b/src/docs/auth/login.md @@ -71,15 +71,25 @@ If you want to use a couple of fields from the user within your application, you Leaf uses token based authentication by default which uses a JWT to authenticate your users. Sessions are a more common way to authenticate users in fullstack applications. To switch to session based authentication, you can update your auth config: -```php:no-line-numbers +::: code-group + +```php [Leaf] auth()->config('session', true); + +... + +// auth login +``` + +```php:no-line-numbers [Leaf MVC - config/auth.php] + 'session' => true, ``` +::: + With the addition of session auth, `login()` will automatically start a session, but will behave in the same way, which means redirects and any other functionality you need will be left up to you to handle: ```php -auth()->config('session', true); - ... // session is automatically started @@ -111,6 +121,54 @@ auth()->config('session.cookie', [ ]); ``` +## Signing in from OAuth + +Some applications only allow users to sign in using OAuth which means there's no need for users to add emails or passwords. Leaf Auth provides the `fromOAuth()` function which allows you to create a session or token for a user without needing a password. + +```php +$user = Github()->getResourceOwner($token)->toArray(); + +$success = auth()->fromOAuth([ + 'token' => $token, + 'user' => [ + 'name' => $user['name'], + 'email' => $user['email'], + 'avatar' => $user['avatar_url'] + ] +]); +``` + +If the user is successfully saved in the database, a session or token is created for them and the rest of the process is the same as signing up a user normally. If Leaf Auth fails to save the user, the method returns `false`. You can then use the `errors()` method to get the error message. + +```php +$success = auth()->fromOAuth([ + 'token' => $token, + 'user' => [ + 'name' => $user['name'], + 'email' => $user['email'], + 'avatar' => $user['avatar_url'] + ] +]); + +if (!$success) { + $error = auth()->errors(); +} + +// user is authenticated +$user = auth()->user(); +``` + +Everything after this point is the same as signing up a user normally. + +::: info OAuth Token +The `fromOAuth()` method expects an OAuth token to be passed in. This token is usually gotten from the OAuth provider you are using. You can later use this token to make requests to the OAuth provider on behalf of the user. Leaf Auth saves this token so you can retrieve it later using the `auth()->oauthToken()` method. + +```php +$token = auth()->oauthToken(); +``` + +::: + ## Auth with no password Leaf Auth usually expects a password field to authenticate users. This is necessary because most applications require a password to authenticate users. The field is usually named `password`, however, you can configure Leaf Auth to expect a different field: diff --git a/src/docs/auth/signup.md b/src/docs/auth/signup.md index eaa34013..a3f25535 100644 --- a/src/docs/auth/signup.md +++ b/src/docs/auth/signup.md @@ -98,14 +98,61 @@ if ($success) { } ``` +## Signing up from OAuth + +Some applications only allow users to sign up using OAuth which means there's no need for users to add emails or passwords. Leaf Auth provides the `fromOAuth()` function which allows you to save users to your database and immediately authenticate them. + +```php +$user = Github()->getResourceOwner($token)->toArray(); + +$success = auth()->fromOAuth([ + 'token' => $token, + 'user' => [ + 'name' => $user['name'], + 'email' => $user['email'], + 'avatar' => $user['avatar_url'] + ] +]); +``` + +If the user is successfully saved in the database, a session or token is created for them and the rest of the process is the same as signing up a user normally. If Leaf Auth fails to save the user, the method returns `false`. You can then use the `errors()` method to get the error message. + +```php +$success = auth()->fromOAuth([ + 'token' => $token, + 'user' => [ + 'name' => $user['name'], + 'email' => $user['email'], + 'avatar' => $user['avatar_url'] + ] +]); + +if (!$success) { + $error = auth()->errors(); +} + +// user is authenticated +$user = auth()->user(); +``` + +`fromOAuth()` automatically turns off password encoding, so you don't need to worry doing it manually. If you want to turn off password encoding for normal signups, you can find the documentation [here](#password-hashing). + ## Unique fields There are some fields in your database that should not be repeated for different users. For example, you wouldn't want two users to have the same email address. You can configure Leaf Auth to check for unique fields when a user is being registered: -```php:no-line-numbers +::: code-group + +```php:no-line-numbers [Leaf] auth()->config('unique', ['email', 'username']); ``` +```php:no-line-numbers [Leaf MVC - config/auth.php] +'unique' => ['email', 'username'] +``` + +::: + Now if a user tries to register with an email or username that already exists in the database, Leaf Auth will return an error. You can get the error message using the `errors()` method. ```php @@ -156,6 +203,15 @@ These are the available options you can pass to `password.encode`: - `null`/`true` - This uses the default encoding method (Leaf\Helpers\Password::hash) - `function` - This uses a custom method. The method should accept a password and return the encoded password. +::: info Watch out +Turning off password encoding does not mean that Leaf will not expect a password field when authenticating users. You will need to turn off password verification as well. If you want to turn off password authentication completely, you can configure Leaf Auth like this: + +```php:no-line-numbers +auth()->config('password.key', false); +``` + +::: + ## Hiding sensitive information The output of Leaf's authentication methods is an object with the user's data and the token or session. By default, the password field is hidden from the user data. This is a security measure to prevent the password from being exposed. diff --git a/src/docs/auth/user.md b/src/docs/auth/user.md index 1f0e8a61..b823e909 100644 --- a/src/docs/auth/user.md +++ b/src/docs/auth/user.md @@ -33,23 +33,34 @@ While this may seem like a lot of work, it's a good way to ensure that your user ## User relationships -Relationships are a way to connect different models in your application. For instance, a user may have many posts, or a user may have many transactions. Using relationships, you can fetch related data from your database. If you have a user model with a one-to-many relationship with a posts model, you can fetch the user's posts using the user object: +Leaf auth comes with a very basic model system that allows you to get/set data related to the current user. For instance, you may want to get all posts by the current user or all transactions by the current user, or maybe add a new purchase to the current user. All these can be done using the user method. ```php -$posts = auth()->user()->posts(); +$posts = auth()->user()->posts()->get(); // will return a Leaf DB instance with posts by the current user // SELECT * FROM posts WHERE user_id = $current_user_id ``` -You can further filter the data by using any of the Leaf DB methods: +If you want to relate a user to a different table, you can do this by calling whatever table your user is related to as a method on the user object. For instance, if you want to grab all user posts from the `posts` table, you can call the `posts()` method on the user object. If you want to grab all user transactions from the `transactions` table, you can call the `transactions()` method on the user object. Once you call the method, it will return a Leaf DB instance which has already been filtered by the user's ID. + +```php +$purchases = auth()->user()->purchases(); +// will return a Leaf DB instance with purchases by the current user + +$purchases->get(); +$purchases->first(); +... +``` + +### Filtering user relationships + +Since a Leaf DB instance is returned, you can further filter the data by using any of the Leaf DB methods: ```php:no-line-numbers $posts = auth()->user()->posts()->where('title', 'like', '%leaf%')->get(); ``` -You can do this by calling whatever table your user is related to as a method on the user object. For instance, if you want to grab all user transactions from the `transactions` table, you can call the `transactions()` method on the user object. If you want to grab all books your user has read from the `read_books` table, you can call the `readBooks()` method on the user object. - -It will return a Leaf DB instance with the related data. You can further filter the data by using any of the Leaf DB methods: +Here are some common examples: ```php:no-line-numbers $posts = auth() @@ -65,6 +76,69 @@ $books = auth() ->first(); ``` +### Creating related data + +If you want to add new data to a database table which should be related to the current user, you can call the table name as a method on the user object and then call the `create()` method on the returned Leaf DB instance. + +```php +auth()->user()->posts()->create([ + 'title' => 'My first post', + 'content' => 'This is my first post' +]); +``` + +This will create a new post in the `posts` table with the `user_id` set to the current user's ID. + +### Updating related data + +If you want to update data related to the current user, you can call the table name as a method on the user object and then call the `update()` method on the returned Leaf DB instance. + +```php +auth() + ->user() + ->purchases() + ->update([ + 'status' => 'completed', + ]) + ->execute(); +``` + +You can also further filter the data before updating it: + +```php +auth() + ->user() + ->purchases() + ->update([ + 'status' => 'completed', + ]) + ->where('status', 'pending') + ->execute(); +``` + +### Deleting related data + +If you want to delete data related to the current user, you can call the table name as a method on the user object and then call the `delete()` method on the returned Leaf DB instance. + +```php +auth() + ->user() + ->purchases() + ->delete() + ->where('status', 'cancelled') + ->execute(); +``` + +You can also delete all related data: + +```php +auth() + ->user() + ->purchases() + ->delete() + ->execute(); +``` + ## Updating a logged-in user Updating user information means allowing users to change their details (like username, email, or password) in your application. It is a very common feature in most applications. diff --git a/src/docs/database/factories.md b/src/docs/database/factories.md index 0b1a4c7a..bae0e069 100644 --- a/src/docs/database/factories.md +++ b/src/docs/database/factories.md @@ -1,5 +1,9 @@ # DB Factories + + Factories are a way to conveniently generate large amounts of database records. Instead of manually specifying the attributes for each model seed, you can use factories to define the attributes for each model. This way, you can easily generate a large number of records with random data. ## Creating a Factory diff --git a/src/docs/database/index.md b/src/docs/database/index.md index 200ec6c2..5424240b 100644 --- a/src/docs/database/index.md +++ b/src/docs/database/index.md @@ -66,18 +66,45 @@ db()->connect([ ]); ``` -The `connect()` method takes an array of connection details for your database as its argument. Depending on the database system you're using, you'll need to provide different connection details. For example, here's how you can connect to a SQLite database: +The `connect()` method takes an array of connection details for your database as its argument. Depending on the database system you're using, you'll need to provide different connection details. +Here are some examples of how you can connect to different databases: -```php +::: code-group + +```php [MySQL] +db()->connect([ + 'host' => '127.0.0.1', + 'username' => 'root', + 'password' => '', + 'dbname' => 'Leaf', +]); +``` + +```php [PostgreSQL] +db()->connect([ + 'dbtype' => 'pgsql', + 'host' => '127.0.0.1', + 'username' => 'root', + 'password' => '', + 'dbname' => 'Leaf', + 'port' => '5432', +]); +``` + +```php [SQLite] db()->connect([ 'dbtype' => 'sqlite', 'dbname' => 'db.sqlite', ]); ``` -If you have a environment file, you can use it to store your database connection details. Here's an example of how you can connect to a MySQL database using an environment file: +::: + +If you are using Leaf MVC, we have already set up everything for you. All you need to do is to head over to your `.env` file and set up your database connection details. + +::: code-group -```txt +```txt [MySQL] DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 @@ -86,12 +113,24 @@ DB_USERNAME=root DB_PASSWORD= ``` -Using the environment file, you can connect to the database like this: +```txt [PostgreSQL] +DB_CONNECTION=pgsql +DB_HOST=127.0.0.1 +DB_PORT=5432 +DB_DATABASE=LeafMVC +DB_USERNAME=root +DB_PASSWORD= +``` -```php -db()->autoConnect(); +```txt [SQLite] +DB_CONNECTION=sqlite +DB_DATABASE=/absolute/path/to/database.sqlite ``` +::: + +Remember to head over to `public/index.php` and uncomment the line that says `\Leaf\Database::initDb();`. This will automatically connect to your database using the details in your environment file. + ## Writing simple queries Once you've connected to a database, you can start writing queries to interact with it. Queries are the commands you run on your database to get, insert, update or delete data. Leaf DB provides a simple way to run queries using the query builder, but also allows you to run raw SQL queries. @@ -147,22 +186,3 @@ There may be times when you want to get a single value from a query that returns ```php $user = db()->query('SELECT * FROM users')->first(); ``` - -## Leaf DB + MVC - -Leaf MVC comes with a more structured way to work with databases. You can use models to interact with your database. This makes it easier to manage your database operations and keep your code clean. However, you can still use the query builder to run raw queries if you need to. - -To get started, you need to set up your database connection in your environment file. - -```txt -DB_CONNECTION=mysql -DB_HOST=127.0.0.1 -DB_PORT=3306 -DB_DATABASE=LeafMVC -DB_USERNAME=root -DB_PASSWORD= -``` - -From there, you just need to head over to `public/index.php` and uncomment the line that says `\Leaf\Database::initDb();`. This will automatically connect to your database using the details in your environment file. - -If you have multiple databases, Leaf DB will connect to your database which has been labelled as your default database. Leaf DB does not yet support multiple database connections, but of course, you can manually create multiple instances of Leaf DB. diff --git a/src/docs/database/migrations.md b/src/docs/database/migrations.md index f264676f..bae0620b 100644 --- a/src/docs/database/migrations.md +++ b/src/docs/database/migrations.md @@ -1,6 +1,10 @@ # Migrations -Laravel database migrations are like version control for your database. They allow you to easily create, modify, or delete tables and columns in your database in a structured way, without having to manually write SQL queries. + + +Database migrations are like version control for your database. They allow you to easily create, modify, or delete tables and columns in your database in a structured way, without having to manually write SQL queries. When you make changes to your database like adding a new table or column, modifying an existing column's data type, or changing a relationship between tables, database migrations allow you to propagate those changes to all instances of your database. diff --git a/src/docs/database/schema.md b/src/docs/database/schema.md index 94cb22e8..f9026b63 100644 --- a/src/docs/database/schema.md +++ b/src/docs/database/schema.md @@ -1,8 +1,12 @@ -# Schema +# Schema v0.X -Schema is a simple, yet powerful tool for generating database migrations from JSON data. Instead of dealing with the stress of writing your database migrations from scratch and thinking about all the types of your data, you can simply create a JSON file with sample data and let Leaf do the rest. +JSON Schema is a simple, yet powerful tool for generating database migrations from JSON data. Instead of dealing with the stress of writing your database migrations from scratch and thinking about all the types of your data, you can simply create a JSON file with sample data and let Leaf do the rest. + + ## Writing your schema diff --git a/src/docs/database/seeders.md b/src/docs/database/seeders.md index eba5124c..447a38e7 100644 --- a/src/docs/database/seeders.md +++ b/src/docs/database/seeders.md @@ -2,6 +2,10 @@ + + Database seeds are a way to populate a database with initial data. This initial data can be used to set up default values or pre-populate a database with test data. Database seeds typically contain small amounts of data, such as default settings, test data, or sample records. Seeds are often used in conjunction with database migrations. After the database schema has been updated or modified, seeds can be used to populate the new or modified tables with initial data. This can be especially useful for testing and development, as it allows developers to work with a pre-populated database without having to manually enter test data. diff --git a/src/docs/http/cookies.md b/src/docs/http/cookies.md index f828aba8..321c7d1d 100644 --- a/src/docs/http/cookies.md +++ b/src/docs/http/cookies.md @@ -24,7 +24,7 @@ composer require leafs/cookie Since cookies are sent to the client's browser as part of the response, Leaf provides a direct way to set cookies on your response. You can directly call `withCookie()` on your response object to set a cookie. -```php +```php:no-line-numbers response()->withCookie('name', 'Fullname'); ``` @@ -70,7 +70,7 @@ The `set()` method allows you to set cookies with more advanced options like exp When you send cookies to the client, they are stored in your users' browser and automatically sent back to your app on every request. You can read these cookies using the `get()` method. -```php +```php:no-line-numbers $name = cookie()->get('name'); ``` @@ -78,7 +78,7 @@ This method takes in the cookie name and returns the cookie value. If the cookie You can also get all cookies at once using the `all()` method. -```php +```php:no-line-numbers $cookies = cookie()->all(); ``` @@ -109,6 +109,6 @@ cookie()->delete('name'); You may also choose to delete all your cookies, for instance if you detect an authentication or authorization breech in your application. You can do this using the `deleteAll()` method on Leaf cookies. -```php +```php:no-line-numbers cookie()->deteleAll(); ``` diff --git a/src/docs/http/flash.md b/src/docs/http/flash.md index 9cc41a22..b5e4c61a 100644 --- a/src/docs/http/flash.md +++ b/src/docs/http/flash.md @@ -1,51 +1,57 @@ # Session Flash -Session flash allows you to store data in the session for a single request. This is useful for scenarios like displaying a message after a form submission. Leaf provides a straightforward way to work with flash messages. +Session flash allows you to store data in the session for a single request. This is useful for scenarios like displaying a message after a form submission or passing data from one request to another. ## Adding a new flash -You can set a new flash item using the `set()` method. It method accepts two arguments: +You can add a new flash message to a response using the `withFlash()` method. This method accepts two arguments: -- The item to flash -- The key to save it under. The key is optional and defaults to `message`. +- The name of the flash message +- The value of the flash message + +```php:no-line-numbers +response()->withFlash('message', 'something'); +``` + +You can chain the `withFlash()` method with your main response methods to return a response with a flash message. ```php -flash()->set('This is my message'); -flash()->set('This is my message', 'info'); +response() + ->withFlash('message', 'something') + ->json('...'); ``` You are not limited to strings. You can flash different types of data: ```php -flash()->set($userObject); -flash()->set($userArray); -flash()->set($userString); -flash()->set($userInt); +response()->withFlash('object', $userObject)->json('...'); +response()->withFlash('array', $userArray)->json('...'); +response()->withFlash('string', $userString)->json('...'); +response()->withFlash('int', $userInt)->json('...'); ``` ## Display a flash item To display a flash item, you can use the `display()` method. This method accepts the key of the item to get. If the key is not provided, it defaults to `message`. -```php -echo flash()->display(); +```php:no-line-numbers +$message = flash()->display(); ``` -If you set a flash item with a key, you can pass the key to the `display()` method to get the item. +If you set a flash item with a different key, you can pass the key to the `display()` method to get the item. ```php -flash()->set('This is my message', 'info'); -... - -echo flash()->display('info'); +$message = flash()->display('info'); +$object = flash()->display('object'); +$array = flash()->display('array'); ``` The item will be removed from the session after it has been displayed. ## Manually removing a flash item -You may choose to remove a flash item manually using the `remove()` method. This method accepts the key of the item to remove. +You may choose to remove a flash item manually without displaying it first. You can do this by calling the `remove()` method with the key of the item to remove. -```php +```php:no-line-numbers flash()->remove('info'); ``` diff --git a/src/docs/http/response.md b/src/docs/http/response.md index 3a380131..f99a525a 100644 --- a/src/docs/http/response.md +++ b/src/docs/http/response.md @@ -185,14 +185,20 @@ These responses are used to redirect the user to another URL. You can create a r ::: code-group -```php [Functional Mode] -response()->redirect('https://example.com'); +```php:no-line-numbers [Functional Mode] +response()->redirect('/example'); // path within the app +response()->redirect(['example']); // redirect with route name +response()->redirect('https://example.com'); // redirect to external URL ``` -```php [Leaf Instance] -$app->response()->redirect('https://example.com'); +```php:no-line-numbers [Leaf Instance] +$app->response()->redirect('/example'); // path within the app +$app->response()->redirect(['example']); // redirect with route name +$app->response()->redirect('https://example.com'); // redirect to external URL ``` +::: + ### File download responses It's not so unusual to want to send a file to the user for download. You can create a file download response using the `download()` method. This method accepts 3 parameters: @@ -203,13 +209,13 @@ It's not so unusual to want to send a file to the user for download. You can cre ::: code-group -```php [Functional Mode] +```php:no-line-numbers [Functional Mode] response()->download('path/to/file.pdf'); response()->download('path/to/file.pdf', 'new-filename.pdf', 200); ``` -```php [Leaf Instance] +```php:no-line-numbers [Leaf Instance] $app->response()->download('path/to/file.pdf'); $app->response()->download('path/to/file.pdf', 'new-filename.pdf', 200); @@ -223,11 +229,11 @@ These responses are used when you don't want to return any content to the user. ::: code-group -```php [Functional Mode] +```php:no-line-numbers [Functional Mode] response()->noContent(); ``` -```php [Leaf Instance] +```php:no-line-numbers [Leaf Instance] $app->response()->noContent(); ``` @@ -242,11 +248,11 @@ XML responses can be created using the `xml()` method. This method accepts 2 par ::: code-group -```php [Functional Mode] +```php:no-line-numbers [Functional Mode] response()->xml(''); ``` -```php [Leaf Instance] +```php:no-line-numbers [Leaf Instance] $app->response()->xml(''); ``` @@ -265,7 +271,7 @@ Leaf allows you to set headers for your response directly from the response obje ::: code-group -```php [Functional Mode] +```php:no-line-numbers [Functional Mode] response()->withHeader('Content-Type', 'application/json'); response()->withHeader([ 'Content-Type' => 'application/json', @@ -273,7 +279,7 @@ response()->withHeader([ ]); ``` -```php [Leaf Instance] +```php:no-line-numbers [Leaf Instance] $app->response()->withHeader('Content-Type', 'application/json'); $app->response()->withHeader([ 'Content-Type' => 'application/json', @@ -333,7 +339,7 @@ Leaf allows you to set flash messages for your response using the `withFlash()` - The name of the flash message - The value of the flash message -```php +```php:no-line-numbers response()->withFlash('message', 'something'); ``` diff --git a/src/docs/routing/index.md b/src/docs/routing/index.md index 47d43745..3d624f08 100644 --- a/src/docs/routing/index.md +++ b/src/docs/routing/index.md @@ -110,6 +110,27 @@ app()->get('/home', ['name' => 'home', function () { }]); ``` +You can then redirect to this route using the route name by passing an array with the route name to the `redirect()` method. + +```php:no-line-numbers +response()->redirect(['home']); +``` + +If you want to get details about a route, you can use the `getRoute()` method. + +```php:no-line-numbers +$route = app()->route($routeName); +``` + +This will return an array containing the following information: + +- `pattern`: The route pattern +- `path`: The route path +- `name`: The route name +- `method`: The route method +- `handler`: The route handler +- Any other route options + ## Getting the current route There are times when you need to get the current route which the user is visiting from inside your route handler. You can do this by calling the `getRoute()` method on the router instance. @@ -132,24 +153,16 @@ This method returns an array containing the following information: ## Navigating to another route -There are times when you need to redirect users to another route. For example, after a user logs in, you might want to redirect them to their dashboard. You can do this by calling the `push()` method on the router instance or the `redirect()` method on the response instance. +There are times when you need to redirect users to another route. For example, after a user logs in, you might want to redirect them to their dashboard. You can do this by calling the `redirect()` method on the response instance. -::: code-group - -```php:no-line-numbers [router] -app()->push('/login'); -``` - -```php:no-line-numbers [response] +```php:no-line-numbers response()->redirect('/login'); ``` -::: - -If your route has a name, you can navigate to it by passing the route name in an array to the `push()` method. +If your route has a name, you can navigate to it by passing the route name in an array to the `redirect()` method. ```php:no-line-numbers -app()->push(['home']); +response()->redirect(['home']); ``` ## Routing in Leaf MVC diff --git a/src/docs/security/csrf.md b/src/docs/security/csrf.md index cdf80e7d..40cdbb12 100644 --- a/src/docs/security/csrf.md +++ b/src/docs/security/csrf.md @@ -62,11 +62,29 @@ This will automatically generate a CSRF token using a default secret key and a r If the CSRF token is missing or invalid, the CSRF module will throw an exception, which you can catch and handle as you see fit. +::: info Leaf MVC + CSRF + +Once you install the CSRF module, Leaf will automatically pick up the `config/csrf.php` file and use it to set up CSRF protection for your app, so you don't need to manually call `app()->csrf()` or worry about passing any configuration to the CSRF module. + +::: + ## Protecting your forms To protect your forms from CSRF attacks, you can add the CSRF token to your forms. The CSRF module provides a beautiful `form()` method that generates a hidden input field with the CSRF token. -```blade +::: code-group + +```blade{2} [Leaf Blade] +
+ {{ csrf()->form() }} + + + + +
+``` + +```blade{2} [BareUI]
form(); ?> @@ -76,6 +94,8 @@ To protect your forms from CSRF attacks, you can add the CSRF token to your form
``` +::: + Once this form is submitted, the CSRF module will verify the token and allow the request to go through if the token is valid. Note that you don't need to do anything else to verify the token; the CSRF module handles everything for you. ## X-CSRF-Token Header @@ -99,7 +119,7 @@ This will send a POST request to `/submit` with the CSRF token in the `X-CSRF-To The CSRF module also provides a `token()` method that returns the CSRF token. You can use this method to display the token in your views or to send the token to your frontend. Be careful not to expose the token to the public, as it can be used to bypass CSRF protection. -```php +```php:no-line-numbers $csrfToken = csrf()->token(); ``` @@ -145,13 +165,13 @@ It is not required to change the secret key, but it is recommended to do so if y If you have an environment file, you can set the secret key there. -```txt +```txt:no-line-numbers X_CSRF_SECRET=my-new-secret-key ``` -## Error Handling +## Handling CSRF Failures By default, Leaf will output a built-in error page when a CSRF token is invalid. You can customize the messages shown on this page by updating your `config` object. @@ -176,4 +196,12 @@ app()->csrf([ ]); ``` -You can use this to handle the error in any way you want. +You can use this to handle the error in any way you want, including redirecting the user to a custom error page or logging the error. + +## I keep getting a `Token not found` error + +If you keep getting a `Token not found` error or any error you set in `messages.tokenNotFound`, it means the CSRF token is not being received by the CSRF module. This can happen if you're using a form that doesn't have the CSRF token or if the CSRF token is being removed by a middleware or some other part of your app. + +If you are submitting a form, make sure the form has the CSRF token. You can find an example [here](#protecting-your-forms). + +If you are sending a request via JavaScript, make sure the `X-CSRF-Token` header is being sent with the request. You can find an example [here](#x-csrf-token-header). diff --git a/src/support.md b/src/support.md index f2f9635a..889f8a64 100644 --- a/src/support.md +++ b/src/support.md @@ -14,23 +14,21 @@ import SponsorGroup from '@theme/components/shared/SponsorGroup.vue' # We need your help -![image](https://github.com/user-attachments/assets/0e08dd91-db25-407a-9c17-aec25a01e241) + -Leaf began in 2019 as a simple boilerplate to simplify API development but has since expanded to include a suite of open-source tools like the Leaf PHP framework and [Hanabira](https://hanajs.dev). +Leaf began in 2019 as a boilerplate to supercharge API development but has since expanded into a full framework with libraries like [Hanabira](https://hana.leafphp.dev). -We're committed to keeping Leaf open-source and free, but as the project grows, so do the costs. Maintaining and building new features now takes significant time and resources, mostly covered by our team. To keep Leaf thriving, we need your support for our maintainers 💚 - -Become a sponsor and help us grow the Leaf ecosystem with more features, better support, and ongoing updates while keeping it free and accessible for everyone. +We're committed to keeping Leaf free, but as the project grows, so do the costs. Maintaining and building new features now takes significant time and resources, mostly covered by our team. To keep Leaf thriving, we need your support for our maintainers 💚 ## How to Sponsor You can support Leaf through [GitHub Sponsors](https://github.com/sponsors/leafsphp) or [OpenCollective](https://opencollective.com/leaf), where your contributions go into a transparent fund supporting community initiatives, events, and core Leaf activities. Both recurring monthly sponsorships and one-time donations are welcome. Recurring sponsors also enjoy benefits like logo placements, as outlined in our Sponsorship Tiers. Your support directly fuels the growth and sustainability of Leaf and its community. -## Sponsoring Leaf as a Business +You can also send in USDT through `TK6d2w4EqSDsf2xB2SLcEkfUt3vxADtFmp` using TRC20. -Sponsoring Leaf gives you great exposure to all Leaf developers around the world through our website and GitHub project READMEs. In addition, supporting OSS improves the reputation of your brand, which is an important asset for any company that interacts with developers. +## Sponsoring Leaf as a Business -If you are using Leaf to build a revenue-generating product, it makes business sense to sponsor Leaf's development: **it ensures the project that your product relies on stays healthy and actively maintained.** The exposure and positive brand image in the Leaf community also makes it easier to attract and recruit developers. +Sponsoring Leaf gives you great exposure to all PHP developers around the world through our website and GitHub project READMEs. In addition, supporting OSS improves the reputation of your brand, and also ensures that Leaf stays healthy and actively maintained. You can join our [Premium Sponsorship 🍁](https://opencollective.com/leaf/contribute/premium-sponsor-79271) tier which gives you the following perks: @@ -42,7 +40,7 @@ You can join our [Premium Sponsorship 🍁](https://opencollective.com/leaf/cont ## Past/Present Sponsors -We are grateful to all our sponsors for their generous support. Check out our current sponsors below: +We are grateful to all our sponsors, both past and present for their generous support 💚