Maison  >  Article  >  développement back-end  >  Le guide ultime des services génériques Pionia.

Le guide ultime des services génériques Pionia.

王林
王林original
2024-07-16 20:46:49637parcourir

The Ultimate Guide to Pionia Generic Services.

Le framework Pionia est un framework PHP Rest qui change la façon dont nous développions les plateformes Rest. Contrairement à tous les frameworks existants, il donne à l’ensemble du processus un tout nouveau look, rendant le développement d’API beaucoup plus simple et moins ennuyeux. C'est parce qu'il fonctionne selon un modèle différent et plutôt « nouveau » appelé clair de lune.

En soi, le clair de lune n'est pas une nouvelle architecture/modèle, la plupart des institutions/entreprises/développeurs l'utilisent mais sans le nommer. Mais aujourd'hui, nous ne parlons pas du clair de lune, vous pouvez le lire dans mon autre article ici et même laisser vos commentaires.

Pour démarrer un nouveau projet Pionia, vous devez exécuter la commande suivante en supposant que Composer est déjà configuré.

Créons une todo_app.

composer create-project pionia/pionia-app todo_app

Vous pouvez également exécuter le même projet en utilisant la commande pionia comme ci-dessous :

php pionia serve

Pour surveiller vos logs en temps réel, ouvrez un deuxième terminal et exécutez la commande suivante :

tail -f server.log

Contexte des services.

Les services du framework Pionia sont le cœur, probablement la seule partie sur laquelle vous passerez le plus clair de votre temps à développer vos API. Tous les services normaux de Pionia étendent PioniaRequestBaseRestService. Un service normal à Pionia peut ressembler à ceci.

namespace application\services;


use Exception;
use Pionia\Request\BaseRestService;
use Pionia\Response\BaseResponse;
use Porm\Porm;

class UserService extends BaseRestService
{
    /**
     * @throws Exception
     */
    protected function login(array $data): BaseResponse
    {
        // password and username are required, without them we won't proceed even
        $this->requires(["username", "password"]);

        $username = $data["username"];
        $password = password_hash($data['password'], PASSWORD_DEFAULT);

        $user = Porm::from('user')->get(['username' => $username, 'password' => $password]);
        //You can do more here, maybe generate a JWT token or add more checks
        // for example if the user is an active or not
        if ($user) {
            return BaseResponse::JsonResponse(0, "Login successful", $user);
        }
        throw new Exception("User with the given username and password not found");
    }

}

Après avoir créé votre service, vous devez l'enregistrer dans le commutateur qui le gérera désormais. Si vous ne connaissez pas les commutateurs dans Pionia, vous pouvez en savoir plus ici dans la documentation. Alors, rendez-vous dans notre dossier switchs, probablement dans le MainAppSwitch.php si vous n'en avez pas encore créé un autre, et enregistrez le service ci-dessus comme ci-dessous dans la méthode registerServices

     /**
     * Register your services here.
     *
     * @return array
     */
    public function registerServices(): array
    {
        return [
            'user' => new UserService(),
            'todo' => new TodoService()
        ];
    }

C'est ainsi que votre service sera désormais découvert automatiquement par le noyau. Dans des paramètres typiques, vous auriez ajouté un routeur et un contrôleur à mapper à ce service, mais Pionia aborde les choses différemment. N'oubliez pas que vous pouvez enregistrer le même service sur plusieurs commutateurs. C'est ainsi que nous réalisons le concept de gestion des versions d'API puisque chaque commutateur est géré par son point de terminaison API. Par défaut, MainAppSwitch est accessible sur /api/v1/.

Dans vos demandes, vous pouvez pointer vers ce service en envoyant ce qui suit.

// POST http://localhost:8000/api/v1/
{
    "SERVICE": "user",
    "ACTION": "login",
    "username": "pionia",
    "password": "pionia1234"
}

Si vous remarquez, ACTION est le nom de l'action/méthode que nous avons créée dans notre SERVICE/service/classe que nous avons baptisé nom d'utilisateur lors de l'inscription.

C'est ainsi que fonctionnent les services normaux à Pionia.

Vous trouverez ci-dessous un service complet qui exécute CRUD à Piona. Il est basé sur la table simple suivante appelée todos dans une base de données MySQL appelée todo_db.

create table todo_db.todos
(
    id          int auto_increment primary key,
    title       varchar(200)                        not null,
    description text                                null,
    created_at  timestamp default CURRENT_TIMESTAMP null
) engine = InnoDB;
use Exception;
use Pionia\Request\BaseRestService;
use Pionia\Request\PaginationCore;
use Pionia\Response\BaseResponse;
use Porm\exceptions\BaseDatabaseException;
use Porm\Porm;

class TodoService extends BaseRestService
{
    /**
     * Returns all todos
     * @throws Exception
     */
    public function list(): BaseResponse
    {
        $result = Porm::table('todos')
            ->using('db')
            ->columns(['id', 'title', 'description', 'created_at'])
            ->all();

        return BaseResponse::JsonResponse(0, null, $result);
    }

    /**
     * Returns a single todo
     * @throws Exception
     */
    public function details(array $data): BaseResponse
    {
        $this->requires(['id']);
        $id = $data['id'];

        $result = Porm::table('todos')
            ->using('db')
            ->columns(['id', 'title', 'description', 'created_at'])
            ->get(['id' => $id]);

        return BaseResponse::JsonResponse(0, null, $result);
    }

    /**
     * Creates a new todo
     * @throws Exception
     */
    public function create(array $data): BaseResponse
    {
        $this->requires(['title', 'description']);
        $title = $data['title'];
        $description = $data['description'];

        $result = Porm::table('todos')
            ->save(['title' => $title, 'description' => $description]);

        return BaseResponse::JsonResponse(0, 'Todo created successfully', $result);
    }

    /**
     * Updates a todo
     * @throws Exception
     */
    public function update(array $data): BaseResponse
    {
        $this->requires(['id']);

        $id = $data['id'];

        $todo = Porm::table('todos')
            ->get($id); // similar to `get(['id' => $id])`

        // if the todo is not found, we throw an exception
        if (!$todo) {
            throw new BaseDatabaseException('Todo not found');
        }

        $description = $data['description'] ?? $todo->description;
        $title = $data['title'] ?? $todo->title;

        // we update in a transaction as below
        $result= null;
        Porm::table('todos')
            ->inTransaction(function () use ($description, $title, $id, &$result) {
                Porm::table('todos')
                    ->update(['description' => $description, 'title' => $title], $id);

                $result = Porm::table('todos')
                    ->get($id);
            });

        return BaseResponse::JsonResponse(0, "Todo $id updated successfully", $result);
    }

    /**
     * Deletes a todo
     * @throws Exception
     */
    public function delete(array $data): BaseResponse
    {
        $this->requires(['id']);
        $id = $data['id'];

        $todo = Porm::table('todos')
            ->get($id);

        if (!$todo) {
            throw new BaseDatabaseException('Todo not found');
        }

        $deleted = false;
        Porm::table('todos')
            ->inTransaction(function () use ($id, &$deleted) {
                Porm::table('todos')
                    ->delete($id);
                $deleted = true;
            });
        if (!$deleted) {
            throw new BaseDatabaseException('Todo not deleted');
        }
        return BaseResponse::JsonResponse(0, "Todo $id deleted successfully");
    }

    /**
     * Returns a random todo object if the size is not defined or 1,
     * else returns an array of random todos
     * @throws Exception
     */
    public function random($data): BaseResponse
    {
        $size = $data['size'] ?? 1;

        $result = Porm::table('todos')
            ->random($size);

        return BaseResponse::JsonResponse(0, null, $result);
    }

    /**
     * Returns a paginated list of todos
     * @throws Exception
     */
    public function paginatedList(array $data): BaseResponse
    {
        $offset = $data['offset'] ?? 0;
        $limit = $data['limit'] ?? 3;

        $paginator = new PaginationCore($data, 'todos', $limit, $offset, 'db');
        $result = $paginator->paginate();

        return BaseResponse::JsonResponse(0, null, $result);
    }
}

Et puisque notre TodoService est déjà enregistré, c'est tout ce que nous devons faire, pas d'ajout d'itinéraires supplémentaires, pas d'ajout de contrôleurs, commencez simplement à exécuter les actions dans vos demandes et vous devriez obtenir des réponses uniformes pour toutes les actions ci-dessus.

Cependant, ce n'est pas beaucoup à faire, et la seule chose à faire à Pionia (services du bâtiment), toutes les actions ci-dessus dans notre TodoService peuvent être omises et nous obtenons toujours la même fonctionnalité, c'est là que notre Les services génériques entrent en jeu !

Service Todo, la manière générique.

Si votre logique ne consiste pas plus à créer, supprimer, paginer, lister, mettre à jour, supprimer ou récupérer, alors les services génériques pourraient être tout ce dont vous avez besoin.

Pionia fournit à la fois des services génériques et des mixins à utiliser. Les mixins peuvent être assemblés pour mettre en place l’intégralité de votre nouveau service générique.

Les mixins fournis incluent ListMixin, CreateMixin, DeleteMixin, UpdateMixin, RandomMixin et RetrieveMixin. Sous le capot, même les services génériques combinent simplement ces Mixins tout en étendant GenericService.

Les services génériques fournis incluent RetrieveCreateUpdateService, RetrieveListCreateService, RetrieveListCreateUpdateDeleteService, RetrieveListDeleteService, RetrieveListRandomService, RetrieveListUpdateDeleteService, RetrieveListUpdateService et UniversalGenericService.

Si les génériques ci-dessus ne combinent pas les mixins comme vous le souhaitez, vous pouvez étendre GenericService et appeler tous les mixins que vous souhaitez utiliser, créant ainsi votre service générique personnalisé.

N'oubliez pas que pour utiliser des mixins, vous devez étendre PioniaGenericsBaseGenericService et non le BaseRestService normal que nous avons étendu auparavant. N'oubliez pas non plus que les mixins ne sont que des traits PHP et doivent être utilisés de cette façon.

Pour refactoriser notre TodoService, nous aurons besoin du dernier service générique mentionné, UniversalGenericService car il utilise tous les mixins définis.

Commençons par changer la classe que nous étendons. Refactorisez ceci comme ci-dessous

use Pionia\Generics\UniversalGenericService;
// ... rest of the imports

class TodoService extends UniversalGenericService
{
// ... rest of your actions
}

Before we do anything, let's first define the table we want to target in the database. We use the $table property for this. This is a compulsory feature and must be defined for all generic views.

use Pionia\Generics\UniversalGenericService;
// ... rest of the imports

class TodoService extends UniversalGenericService
{
   public string $table = "todo";
// ... rest of your actions
}

Secondly, from our list action, we are defining columns we want to return, however, we are defining all. If you want to return a certain range of columns only, we define the $listColumns(which defaults to all) and pass the columns we want to return. Let's just still pass all though it is the default behavior of the service.

use Pionia\Generics\UniversalGenericService;
// ... rest of the imports

class TodoService extends UniversalGenericService
{
    public string $table = "todo";
    public ?array $listColumns = ['id', 'title', 'description', 'created_at'];

// ... rest of your actions
}

At this point, we can delete the list action from our service. That's complete!

Our second target action is now details. This one can be replaced by defining the $pk_field which defaults to id. Since our primary key field for our todo table is also id, we don't need to define it, we just need to delete it too! Remember, this one also uses the defined $listColumns for columns to return from the DB.
The RetrieveMixin also defines another sister action to this called retrieve, so in your request, you can use ACTION as details or retrieve, the two will perform the same thing.
Since we already have all we need, we can drop the details action too!

Our third action is create. For this, we must define the $createColumns to define those columns we shall be looking for from the request(required) to create a record. Let's add the property now.

use Pionia\Generics\UniversalGenericService;
// ... rest of the imports

class TodoService extends UniversalGenericService
{
    public string $table = "todo";
    public ?array $listColumns = ['id', 'title', 'description', 'created_at'];
    public ?array $createColumns = ['title', 'description'];

// ... rest of your actions
}

After adding, go ahead and delete it too!

Our fourth action is update. For this, we require the $pk_field and can also optionally define the $updateColumns. If undefined, the responsible mixin checks if any of the properties were defined in the request, and will update only those.
Let's add the $updateColumns and give it the only properties we intend to update.

use Pionia\Generics\UniversalGenericService;
// ... rest of the imports

class TodoService extends UniversalGenericService
{
    public string $table = "todo";
    public ?array $listColumns = ['id', 'title', 'description', 'created_at'];
    public ?array $createColumns = ['title', 'description'];
    public ?array $updateColumns = ['title', 'description'];

// ... rest of your actions
}

We can now drop the update action too!

For our fifth action, delete, we only need the $pk_field which is by default id, so we shall be checking if id was passed in the request, and then we delete the associated record. So, just delete it, we already have all we need!

Now to our sixth action, random, this also uses the $listColumns to determine the columns to fetch from the DB per record. We already have out property defined, so, just drop it too!

For our seventh action, paginatedList, we can drop it, and in any request, we target our list action, but we define any of the following pairs of keys in our request.

  1. limit and offset on the request object level.
{
   "SERVICE": "todo",
   "ACTION": "list",
   "limit": 3,
   "offset": 0
}
  1. PAGINATION or pagination object on the request with limit and offset keys.
{
   "SERVICE": "todo",
   "ACTION": "list",
   "PAGINATION": {
      "limit": 3,
      "offset": 0,
   }
}
  1. SEARCH or search object on the request object with limit and offset keys.
{
   "SERVICE": "todo",
   "ACTION": "list",
   "SEARCH": {
      "limit": 3,
      "offset": 0
   }
}

Note: Both the limit and offset keys must be defined for pagination to kick in.

And just like that, our service now has been reduced to the following.

use Pionia\Generics\UniversalGenericService;

class TodoService extends UniversalGenericService
{
    public string $table = "todo";
    public ?array $listColumns = ['id', 'title', 'description', 'created_at'];
    public ?array $createColumns = ['title', 'description'];
    public ?array $updateColumns = ['title', 'description'];
}

Let's do a little more cleanup. As we had mentioned earlier, if we are listing all columns from our table, then we don't need to define the $listColumns property, let's remove that too.

use Pionia\Generics\UniversalGenericService;

class TodoService extends UniversalGenericService
{
    public string $table = "todo";
    public ?array $createColumns = ['title', 'description'];
    public ?array $updateColumns = ['title', 'description'];
}

Also, since our update can also discover the columns to update from the request data, let's remove the $updateColumns too!

And we are left with the following as our new TodoService but still exposing the actions of list(all and paginated), create, update, delete, retrieve or details and random

use Pionia\Generics\UniversalGenericService;

class TodoService extends UniversalGenericService
{
    public string $table = "todo";
    public ?array $createColumns = ['title', 'description'];
}

You can also override how we get a single record and multiple records. You might not need it, but sometimes you may need to add where clauses and other conditions as you see fit. For that, you can read about it in this section in the docs.

Also, you may want to add your other actions in the same generic service, this is fully supported and will work as if you're in normal services, however, make sure none of those actions share the names with the provided mixin actions or otherwise you stand a chance of overriding the provided actions.

This also drives to the last point, what if you intend to override the default action? that's also okay! You can also look into it under this section of the docs.

Welcome to Pionia Framework, where we believe in both developer and program performance, writing precise and maintainable codebase with simplicity both at the front end and the back end!

Let me hear what you say about the Pionia Framework specifically about generic services. Happy coding!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn