# Getting started

The best way to learn is by practicing. Let's create a basic CRUD using the query builder:

# Create project

This guide assumes that you are using Visual Studio Code (opens new window) as your text editor and Ubuntu as your operating system. If you are a Windows user, it is recommended to use Ubuntu with WSL.

composer create-project phenixphp/phenix phenix

// Go to root path
cd phenix

// Open VS Code
code .

Edit the database configuration variables to establish a connection in the .env file. The default database connection is mysql.

DB_CONNECTION=mysql
DB_DATABASE=phenix
DB_USERNAME=root
DB_PASSWORD=secret

# Create migration

Open a new terminal using the shortcut Ctrl + `, and run a Phenix command.

php phenix make:migration CreateUsersTable

The command will create a migration at the following path:

database/migrations/20230930111521_create_users_table.php

Copy and paste the user table structure. It should look like this:

<?php

declare(strict_types=1);

use Phenix\Database\Migration;

class CreateUsersTable extends Migration
{
    public function up(): void
    {
        $table = $this->table("users");
        $table->addColumn('name', 'string', ['limit' => 100]);
        $table->addColumn('email', 'string', ['limit' => 100]);
        $table->create();
    }

    public function down(): void
    {
        $this->table('users')->drop()->save();
    }
}

Run the migrations with the following command:

php phenix migrate

Once the migrations have been run, you will see output like the following:

using migration paths
 - /home/obarbosa/php/phenix/database/migrations
using seed paths
using environment default
using database phenix
ordering by creation time

 == 20230930111521 CreateUsersTable: migrating
 == 20230930111521 CreateUsersTable: migrated 0.0141s

All Done. Took 0.0381s

# Create user seeder

We need some users created in the database. The command to create the seeder is the following:

php phenix make:seeder UsersSeeder

Copy the seeder code. It should look like this:

<?php

declare(strict_types=1);

use Phenix\Database\Seed;

class UsersSeeder extends Seed
{
    public function run(): void
    {
        $users = $this->table("users");

        $users->insert([
            [
                'name' => 'Rasmus Ledford',
                'email' => 'rasmus.ledford@php.net',
            ],
            [
                'name' => 'Nikita Popov',
                'email' => 'nikita.popov@php.net',
            ],
        ])->saveData();
    }
}

Run the seeder to insert the data:

php phenix seed:run

When the data has been inserted, you will see a result like the following:

using migration paths
 - /home/obarbosa/php/phenix/database/migrations
using seed paths
 - /home/obarbosa/php/phenix/database/seeds
warning no environment specified, defaulting to: default
using database phenix

 == UsersSeeder: seeding
 == UsersSeeder: seeded 0.0234s

All Done. Took 0.0240s

# Create controller

Now let's create the user controller to create the CRUD:

php phenix make:controller UserController -a

The controller will have the following content:

<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Amp\Http\Server\Request;
use Amp\Http\Server\Response;
use Amp\Http\HttpStatus;
use Phenix\Http\Controller;

class UserController extends Controller
{
    public function index(): Response
    {
        return response()->plain('Hello, world!' . PHP_EOL);
    }

    public function store(Request $request): Response
    {
        return response()->json([], HttpStatus::CREATED);
    }

    public function show(Request $request): Response
    {
        return response()->json([], HttpStatus::OK);
    }

    public function update(Request $request): Response
    {
        return response()->json([], HttpStatus::OK);
    }

    public function delete(Request $request): Response
    {
        return response()->json([], HttpStatus::OK);
    }
}

# The index method

This method allows us to obtain all the users created in the database. At this point, we have two options: the query brings all the data, or the most optimal and common, paginate the records. The first step will be to register the user index route in the routes/api.php file:

use App\Http\Controllers\UserController;
use Phenix\Facades\Route;

// ...

Route::get('/users', [UserController::class, 'index']);

If you see any similarity with Laravel routes, it's not a coincidence; the elegant syntax inspires us all.

Now let's add code to the index method. The code should look like this:

public function index(Request $request): Response
{
    $users = DB::table('users')->paginate($request->getUri());

    return response()->json($users);
}

The first thing to mention is that we inject the request into the index method, so the paginator has access to the URL variables to establish which page will be queried, and if there are query parameters, they will be loaded to the URLs that are generated in the response. To get all the data in the users table, just use the get method instead of the paginate method.

Open the terminal using the shortcut Ctrl + Shift + `, and run the server.

php public/index.php

At this point, you can use a client like Postman or Thunder integrated as a VS Code extension, and send a request to the URL http://127.0.0.1:1337/users. The server response should look like the following:

{
    "data": {
        "path": "http://127.0.0.1:1337/users",
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 2,
        "first_page_url": "http://127.0.0.1:1337/users?page=1",
        "last_page_url": "http://127.0.0.1:1337/users?page=1",
        "prev_page_url": null,
        "next_page_url": null,
        "from": 1,
        "to": 2,
        "data": [
            {
                "id": 1,
                "name": "Rasmus Ledford",
                "email": "rasmus.ledford@php.net"
            },
            {
                "id": 2,
                "name": "Nikita Popov",
                "email": "nikita.popov@php.net"
            }
        ],
        "links": [
            {
                "url": "http://127.0.0.1:1337/users?page=1",
                "label": 1
            }
        ]
    }
}

# The store method

The store method will allow us to add new users. Let's write the code. Corresponding route:

Route::post('/users', [UserController::class, 'store']);

Because we are using the query builder, first the insertion is executed and then a query to obtain the inserted record. In later versions, the last inserted ID will be obtained. Additionally, with the use of models, this process is totally transparent; the code will be very simple and powerful.

public function store(Request $request): Response
{
    $data = json_decode($request->getBody()->read(), true);

    DB::table('users')->insert($data);

    $user = DB::table('users')
        ->whereEqual('email', $data['email'])
        ->first();

    return response()->json($user, HttpStatus::CREATED);
}

Because the routes are loaded at server startup time, it is necessary to restart the server. Press Ctrl + C and run the server again:

php public/index.php

Again, use your favorite client and send the necessary data to create a user. The response will contain the data of the new record:

{
    "data": {
        "id": 3,
        "name": "John Doe",
        "email": "john.doe@email.com"
    }
}

Note
Hot reloading feature will be added very soon.

# The show method

This method will help us when we want to consult a record by its identifier:

Route::get('/users/{user}', [UserController::class, 'show']);

Remember that every time we make changes to our code, we must restart the server.

use Phenix\Http\Attributes;

// ...

public function show(Request $request): Response
{
    $attributes = Attributes::fromRequest($request);

    $user = DB::table('users')
        ->whereEqual('id', $attributes->integer('user'))
        ->first();

    return response()->json($user, HttpStatus::OK);
}

The JSON response:

{
    "data": {
        "id": 3,
        "name": "John Doe",
        "email": "john.doe@email.com"
    }
}

# The update method

Now let's update the existing records:

Route::patch('/users/{user}', [UserController::class, 'update']);

We will only update the name for practical purposes.

public function update(Request $request): Response
{
    $attributes = Attributes::fromRequest($request);
    $data = json_decode($request->getBody()->read(), true);

    DB::table('users')
        ->whereEqual('id', $attributes->integer('user'))
        ->update(['name' => $data['name']]);

    $user = DB::table('users')
        ->whereEqual('id', $attributes->integer('user'))
        ->first();

    return response()->json($user, HttpStatus::OK);
}

The client will respond to us with updated data:

{
    "data": {
        "id": 3,
        "name": "John Edison Doe",
        "email": "john.doe@email.com"
    }
}

# The delete method

Finally, the method to delete records from the database:

Route::delete('/users/{user}', [UserController::class, 'delete']);

The response will be a simple message:

public function delete(Request $request): Response
{
    $attributes = Attributes::fromRequest($request);

    DB::table('users')
        ->whereEqual('id', $attributes->integer('user'))
        ->delete();

    return response()->json(['message' => 'Ok'], HttpStatus::OK);
}

We have completed this short quick start guide to show fundamental concepts of the framework: routes and controllers, requests, and running the server. In the following sections, concepts about the architecture of the framework and its characteristics will be deepened.

Important
If you liked Phenix and you have seen the potential and the great opportunity we have in the PHP community, I invite you to give it a star on GitHub (opens new window) and contribute to the development of this powerful framework.