Magazine Gadgets

Travailler avec des services tiers dans laravel

Publié le 14 juin 2023 par Mycamer

Donc, il y a un peu plus de deux ans, j’ai écrit un tutoriel sur la façon dont vous devriez travailler avec des services tiers dans Laravel. À ce jour, c’est la page la plus visitée de mon site Web. Cependant, les choses ont changé au cours des deux dernières années, et j’ai décidé d’aborder à nouveau ce sujet.

Je travaille donc avec des services tiers depuis si longtemps que je ne me souviens pas quand je ne l’étais pas. En tant que développeur junior, j’ai intégré l’API dans d’autres plateformes comme Joomla, Magento et WordPress. Maintenant, il s’intègre principalement dans mes applications Laravel pour étendre la logique métier en s’appuyant sur d’autres services.

Ce tutoriel décrira comment j’aborde généralement l’intégration avec une API aujourd’hui. Si vous avez lu mon tutoriel précédent, continuez à lire car certaines choses ont changé – pour ce que je considère comme de bonnes raisons.

Commençons par une API. Nous avons besoin d’une API à intégrer. Mon tutoriel d’origine intégrait PingPing, une excellente solution de surveillance de la disponibilité de la communauté Laravel. Cependant, je veux essayer une API différente cette fois.

Pour ce tutoriel, nous utiliserons le API Planetscale. Planetscale est un service de base de données incroyable que j’utilise pour rapprocher mes opérations de lecture et d’écriture de mes utilisateurs dans le travail quotidien.

Que va faire notre intégration ? Imaginons que nous ayons une application qui nous permet de gérer notre infrastructure. Nos serveurs passent par Laravel Forge et notre base de données est terminée sur Planetscale. Il n’existe aucun moyen propre de gérer ce flux de travail, nous avons donc créé le nôtre. Pour cela, nous avons besoin d’une intégration ou deux.

Au départ, j’avais l’habitude de garder mes intégrations sous app/Services; cependant, comme mes applications sont devenues plus étendues et compliquées, j’ai dû utiliser le Services espace de noms pour les services internes, conduisant à un espace de noms pollué. J’ai déplacé mes intégrations vers app/Http/Integrations. Cela a du sens et c’est un truc que j’ai repris de Saloon par Sam Carré.

Maintenant je pourrais utiliser Salon pour mon intégration d’API, mais je voulais expliquer comment je le fais sans package. Si vous avez besoin d’une intégration API en 2023, je vous recommande fortement d’utiliser Saloon. C’est plus qu’incroyable !

Commençons donc par créer un répertoire pour notre intégration. Vous pouvez utiliser la commande bash suivante :

mkdirapp/Http/Integrations/Planetscale

Une fois que nous avons le répertoire Planetscale, nous devons créer un moyen de s’y connecter. Une autre convention de dénomination que j’ai extraite de la bibliothèque Saloon consiste à considérer ces classes de base comme des connecteurs – car leur objectif est de vous permettre de vous connecter à une API spécifique ou à un tiers.

Créez une nouvelle classe appelée PlanetscaleConnector dans le app/Http/Integrations/Planetscale répertoire, et nous pourrons étoffer ce dont cette classe a besoin, ce qui sera très amusant.

Nous devons donc enregistrer cette classe auprès de notre conteneur pour la résoudre ou construire une façade autour d’elle. Nous pourrions enregistrer cela sur un “long” chemin dans un fournisseur de services – mais ma dernière approche consiste à faire en sorte que ces connecteurs s’enregistrent eux-mêmes – en quelque sorte …

declare(strict_types=1);

namespaceApp\Http\Integrations\Planetscale;

useIlluminate\Contracts\Foundation\Application;

useIlluminate\Http\Client\PendingRequest;

useIlluminate\Support\Facades\Http;

finalreadonlyclassPlanetscaleConnector

{

publicfunction__construct(

privatePendingRequest $request,

) {}

publicstaticfunctionregister(Application $app):void

{

$app->bind(

abstract: PlanetscaleConnector::class,

concrete: fn () => newPlanetscaleConnector(

request: Http::baseUrl(

url: '',

)->timeout(

seconds: 15,

)->withHeaders(

headers: [],

)->asJson()->acceptJson(),

),

);

}

}

L’idée ici est donc que toutes les informations sur la façon dont cette classe est enregistrée dans le conteneur résident dans la classe elle-même. Tout ce que le fournisseur de services doit faire est d’appeler la méthode de registre statique sur la classe ! Cela m’a fait gagner beaucoup de temps lors de l’intégration avec de nombreuses API, car je n’ai pas à rechercher le fournisseur et à trouver la bonne liaison, parmi tant d’autres. Je vais dans la classe en question, qui est toute devant moi.

Vous remarquerez qu’actuellement, rien n’est transmis aux méthodes de jeton ou d’URL de base dans la requête. Réglons cela ensuite. Vous pouvez les obtenir dans votre compte Planetscale.

Créez les enregistrements suivants dans votre .env déposer.

PLANETSCALE_SERVICE_ID="your-service-id-goes-here"

PLANETSCALE_SERVICE_TOKEN="your-token-goes-here"

PLANETSCALE_URL="https://api.planetscale.com/v1"

Ensuite, ceux-ci doivent être insérés dans la configuration de l’application. Ceux-ci appartiennent tous à config/services.php car c’est là que les services tiers sont généralement configurés.

return [

// the rest of your services config

'planetscale'=> [

'id'=>env('PLANETSCALE_SERVICE_ID'),

'token'=>env('PLANETSCALE_SERVICE_TOKEN'),

'url'=>env('PLANETSCALE_URL'),

],

];

Maintenant, nous pouvons les utiliser dans notre PlanetscaleConnector selon la méthode du registre.

declare(strict_types=1);

namespaceApp\Http\Integrations\Planetscale;

useIlluminate\Contracts\Foundation\Application;

useIlluminate\Http\Client\PendingRequest;

useIlluminate\Support\Facades\Http;

finalreadonlyclassPlanetscaleConnector

{

publicfunction__construct(

privatePendingRequest $request,

) {}

publicstaticfunctionregister(Application $app):void

{

$app->bind(

abstract: PlanetscaleConnector::class,

concrete: fn () => newPlanetscaleConnector(

request: Http::baseUrl(

url: config('services.planetscale.url'),

)->timeout(

seconds: 15,

)->withHeaders(

headers: [

'Authorization'=>config('services.planetscale.id') ':'config('services.planetscale.token'),

],

)->asJson()->acceptJson(),

),

);

}

}

Vous devez envoyer des jetons à Planetscale au format suivant : service-id:service-tokennous ne pouvons donc pas utiliser la valeur par défaut withToken méthode car elle ne nous permet pas de la personnaliser comme nous en avons besoin.

Maintenant que nous avons créé une classe de base, nous pouvons commencer à réfléchir à l’étendue de notre intégration. Nous devons le faire lors de la création de notre jeton de service pour ajouter les autorisations correctes. Dans notre application, nous souhaitons pouvoir effectuer les opérations suivantes :
Lister les bases de données.
Répertorier les régions de la base de données.
Lister les sauvegardes de la base de données.
Créer une sauvegarde de base de données.
Supprimer la sauvegarde de la base de données.

Ainsi, nous pouvons envisager de les regrouper en deux catégories :
Bases de données.
Sauvegardes.

Ajoutons deux nouvelles méthodes à notre connecteur pour créer ce dont nous avons besoin :

declare(strict_types=1);

namespaceApp\Http\Integrations\Planetscale;

useApp\Http\Integrations\Planetscale\Resources\BackupResource;

useApp\Http\Integrations\Planetscale\Resources\DatabaseResource;

useIlluminate\Contracts\Foundation\Application;

useIlluminate\Http\Client\PendingRequest;

useIlluminate\Support\Facades\Http;

finalreadonlyclassPlanetscaleConnector

{

publicfunction__construct(

privatePendingRequest $request,

) {}

publicfunctiondatabases():DatabaseResource

{

returnnewDatabaseResource(

connector: $this,

);

}

publicfunctionbackups():BackupResource

{

returnnewBackupResource(

connector: $this,

);

}

publicstaticfunctionregister(Application $app):void

{

$app->bind(

abstract: PlanetscaleConnector::class,

concrete: fn () => newPlanetscaleConnector(

request: Http::baseUrl(

url: config('services.planetscale.url'),

)->timeout(

seconds: 15,

)->withHeaders(

headers: [

'Authorization'=>config('services.planetscale.id') ':'config('services.planetscale.token'),

],

)->asJson()->acceptJson(),

),

);

}

}

Comme vous pouvez le voir, nous avons créé deux nouvelles méthodes, databases et backups. Ceux-ci renverront de nouvelles classes de ressources, en passant par le connecteur. La logique peut maintenant être implémentée dans les classes de ressources, mais nous devrons ajouter une autre méthode à notre connecteur ultérieurement.

<?php

declare(strict_types=1);

namespaceApp\Http\Integrations\Planetscale\Resources;

useApp\Http\Integrations\Planetscale\PlanetscaleConnector;

finalreadonlyclassDatabaseResource

{

publicfunction__construct(

privatePlanetscaleConnector $connector,

) {}

publicfunctionlist()

{

//

}

publicfunctionregions()

{

//

}

}

C’est notre DatabaseResource; nous avons maintenant tronqué les méthodes que nous voulons mettre en œuvre. Vous pouvez faire la même chose pour le BackupResource. Cela ressemblera quelque peu.

Ainsi les résultats peuvent être paginés sur le listing des bases de données. Cependant, je ne traiterai pas de cela ici – je m’appuierais sur Saloon pour cela, car sa mise en œuvre pour les résultats paginés est fantastique. Dans cet exemple, nous n’allons pas nous soucier de la pagination. Avant de remplir le DatabaseResourcenous devons ajouter une autre méthode à la PlanetscaleConnector pour bien envoyer les demandes. Pour cela, j’utilise mon package appelé juststeveking/http-helpersqui a une énumération pour toutes les méthodes HTTP typiques que j’utilise.

publicfunctionsend(Method $method, string $uri, array $options = []):Response

{

return$this->request->send(

method: $method->value,

url: $uri,

options: $options,

)->throw();

}

Nous pouvons maintenant revenir à notre DatabaseResource et commencez à remplir la logique de la méthode de liste.

declare(strict_types=1);

namespaceApp\Http\Integrations\Planetscale\Resources;

useApp\Http\Integrations\Planetscale\PlanetscaleConnector;

useIlluminate\Support\Collection;

useJustSteveKing\HttpHelpers\Enums\Method;

useThrowable;

finalreadonlyclassDatabaseResource

{

publicfunction__construct(

privatePlanetscaleConnector $connector,

) {}

publicfunctionlist(string $organization):Collection

{

try {

$response =$this->connector->send(

method: Method::GET,

uri: "/organizations/{$organization}/databases"

);

} catch (Throwable $exception) {

throw $exception;

}

return $response->collect('data');

}

publicfunctionregions()

{

//

}

}

Notre méthode de liste accepte le paramètre organization passer par l’organisation pour répertorier les bases de données. Nous l’utilisons ensuite pour envoyer une requête à une URL spécifique via le connecteur. Envelopper cela dans une instruction try-catch nous permet d’intercepter les exceptions potentielles de la méthode d’envoi des connecteurs. Enfin, nous pouvons renvoyer une collection de la méthode pour l’utiliser dans notre application.

Nous pouvons entrer plus en détail avec cette demande, car nous pouvons commencer à mapper les données des tableaux vers quelque chose de plus contextuellement utile en utilisant les DTO. j’ai écrit sur ça icidonc je ne répéterai pas la même chose ici.

Voyons rapidement le BackupResource pour regarder plus qu’une simple requête get.

declare(strict_types=1);

namespaceApp\Http\Integrations\Planetscale\Resources;

useApp\Http\Integrations\Planetscale\Entities\CreateBackup;

useApp\Http\Integrations\Planetscale\PlanetscaleConnector;

useJustSteveKing\HttpHelpers\Enums\Method;

useThrowable;

finalreadonlyclassBackupResource

{

publicfunction__construct(

privatePlanetscaleConnector $connector,

) {}

publicfunctioncreate(CreateBackup $entity):array

{

try {

$response =$this->connector->send(

method: Method::POST,

uri: "/organizations/{$entity->organization}/databases/{$entity->database}/branches/{$entity->branch}",

options: $entity->toRequestBody(),

);

} catch (Throwable $exception) {

throw $exception;

}

return $response->json('data');

}

}

Notre méthode de création accepte une classe d’entité, que j’utilise pour transmettre des données via l’application si nécessaire. Ceci est utile lorsque l’URL a besoin d’un ensemble de paramètres et que nous devons envoyer un corps de requête.

Je n’ai pas couvert les tests ici, mais j’ai écrit un tutoriel sur la façon de testez JSON: les points de terminaison API à l’aide de PestPHP iciqui aura des concepts similaires pour tester une intégration comme celle-ci.

Je peux créer des intégrations fiables et extensibles avec des tiers en utilisant cette approche. Il est divisé en parties logiques, donc je peux gérer la quantité de logique. En règle générale, j’aurais plus d’intégrations, donc une partie de cette logique peut être partagée et extraite en traits pour hériter du comportement entre les intégrations.



to laravel-news.com


Abonnez-vous à notre page Facebook: https://www.facebook.com/mycamer.net
Pour recevoir l’actualité sur vos téléphones à partir de l’application Telegram cliquez ici: https://t.me/+KMdLTc0qS6ZkMGI0
Nous ecrire par Whatsapp : Whatsapp +44 7476844931



Retour à La Une de Logo Paperblog

A propos de l’auteur


Mycamer Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte

Magazines