MVC (Laravel) où ajouter la logique

137

Disons que chaque fois que je fais une opération CRUD ou que je modifie une relation d'une manière spécifique, je veux aussi faire autre chose. Par exemple, chaque fois que quelqu'un publie un article, je souhaite également enregistrer quelque chose dans une table à des fins d'analyse. Peut-être pas le meilleur exemple, mais en général, il y a beaucoup de cette fonctionnalité "groupée".

Normalement, je vois ce type de logique mis dans les contrôleurs. C'est très bien jusqu'à ce que vous souhaitiez reproduire cette fonctionnalité dans de nombreux endroits. Lorsque vous commencez à entrer dans les partiels, à créer une API et à générer du contenu factice, il devient difficile de garder les choses au sec.

Les façons dont j'ai vu pour gérer cela sont les événements, les référentiels, les bibliothèques et l'ajout aux modèles. Voici ma compréhension de chacun:

Services: c'est là que la plupart des gens mettront probablement ce code. Mon principal problème avec les services est qu'il est parfois difficile d'y trouver des fonctionnalités spécifiques et j'ai l'impression qu'ils sont oubliés lorsque les gens se concentrent sur l'utilisation d'Eloquent. Comment saurais-je que j'ai besoin d'appeler une méthode publishPost()dans une bibliothèque alors que je peux simplement le faire $post->is_published = 1?

La seule condition dans laquelle je vois que cela fonctionne bien est si vous utilisez UNIQUEMENT des services (et si vous rendez Eloquent inaccessible d'une manière ou d'une autre à tous les contrôleurs).

En fin de compte, il semble que cela ne ferait que créer un tas de fichiers supplémentaires inutiles si vos demandes suivent généralement la structure de votre modèle.

Référentiels: d' après ce que je comprends, c'est fondamentalement comme un service mais il y a une interface pour que vous puissiez basculer entre les ORM, dont je n'ai pas besoin.

Evénements: Je vois cela comme le système le plus élégant dans un sens parce que vous savez que les événements de votre modèle seront toujours appelés sur des méthodes Eloquent, vous pouvez donc écrire vos contrôleurs comme vous le feriez normalement. Je peux cependant voir que cela devient compliqué et si quelqu'un a des exemples de grands projets utilisant des événements pour un couplage critique, j'aimerais le voir.

Modèles: Traditionnellement, j'avais des classes qui exécutaient CRUD et géraient également le couplage critique. Cela a en fait facilité les choses car vous saviez que toutes les fonctionnalités autour de CRUD +, tout ce qui devait être fait, était là.

Simple, mais dans l'architecture MVC, ce n'est normalement pas ce que je vois faire. Dans un sens, je préfère cela aux services car c'est un peu plus facile à trouver et il y a moins de fichiers à suivre. Cela peut cependant devenir un peu désorganisé. J'aimerais entendre les inconvénients de cette méthode et pourquoi la plupart des gens ne semblent pas le faire.

Quels sont les avantages / inconvénients de chaque méthode? Est-ce que je manque quelque chose?

Sabrina Leggett
la source
3
Pouvez-vous minimiser votre question?
The Alpha
3
Vous pouvez également vérifier cela .
The Alpha
1
"Comment saurais-je que j'ai besoin d'appeler une méthode publishPost () dans une bibliothèque alors que je peux simplement faire $ post-> is_published = 1?" Documentation?
ceejayoz
l'une des beautés de l'éloquent et de l'ORMS est-il plus facile de travailler avec eux sans beaucoup de documents?
Sabrina Leggett
1
Merci d'avoir publié ceci. Je suis aux prises avec les mêmes problèmes et j'ai trouvé votre message et votre réponse incroyablement utiles. En fin de compte, j'ai décidé que Laravel ne fournissait pas une bonne architecture pour tout ce qui s'étend au-delà d'un site Web Ruby-on-Rails rapide et sale. Trates partout, difficulté à trouver les fonctions des classes et des tonnes de déchets auto-magiques partout. ORM n'a jamais fonctionné et si vous l'utilisez, vous devriez probablement utiliser NoSQL.
Alex Barker

Réponses:

171

Je pense que tous les modèles / architectures que vous présentez sont très utiles tant que vous suivez les principes SOLID .

Pour savoir où ajouter la logique, je pense qu'il est important de se référer au principe de responsabilité unique . Aussi, ma réponse considère que vous travaillez sur un projet moyen / grand. S'il s'agit d'un projet de projection de quelque chose sur une page , oubliez cette réponse et ajoutez-le tout aux contrôleurs ou aux modèles.

La réponse courte est: là où cela a du sens pour vous (avec des services) .

La réponse longue:

Contrôleurs : quelle est la responsabilité des contrôleurs? Bien sûr, vous pouvez mettre toute votre logique dans un contrôleur, mais est-ce la responsabilité du contrôleur? Je ne pense pas.

Pour moi, le contrôleur doit recevoir une requête et renvoyer des données et ce n'est pas le lieu pour mettre des validations, appeler des méthodes db, etc.

Modèles : est-ce un bon endroit pour ajouter une logique comme l'envoi d'un e-mail de bienvenue lorsqu'un utilisateur s'inscrit ou mettre à jour le décompte des votes d'un message? Que faire si vous devez envoyer le même e-mail depuis un autre endroit de votre code? Créez-vous une méthode statique? Et si ces e-mails ont besoin d'informations d'un autre modèle?

Je pense que le modèle devrait représenter une entité. Avec Laravel, j'utiliser uniquement la classe de modèle pour ajouter des choses comme fillable, guarded, tableet les relations (c'est parce que j'utilise le modèle du référentiel, sinon le modèle aurait également les save, update, find, méthodes , etc.).

Repositories (Repository Pattern) : Au début, j'étais très confus par cela. Et, comme vous, je me suis dit "eh bien, j'utilise MySQL et c'est tout.".

Cependant, j'ai équilibré les avantages et les inconvénients de l'utilisation du modèle de référentiel et maintenant je l'utilise. Je pense que maintenant , en ce moment même, je n'aurai plus qu'à utiliser MySQL. Mais, si dans trois ans je dois passer à quelque chose comme MongoDB, la plupart du travail est terminé. Le tout au détriment d'une interface supplémentaire et d'un $app->bind(«interface», «repository»).

Événements ( modèle d'observateur ): les événements sont utiles pour les choses qui peuvent être lancées à n'importe quelle classe à tout moment. Pensez, par exemple, à envoyer des notifications à un utilisateur. Lorsque vous en avez besoin, vous déclenchez l'événement pour envoyer une notification à n'importe quelle classe de votre application. Ensuite, vous pouvez avoir une classe comme UserNotificationEventscelle qui gère tous vos événements déclenchés pour les notifications utilisateur.

Services : Jusqu'à présent, vous avez le choix d'ajouter une logique aux contrôleurs ou aux modèles. Pour moi, il est tout à fait logique d'ajouter la logique dans les services . Avouons-le, Services est un nom sophistiqué pour les classes. Et vous pouvez avoir autant de cours que cela vous convient dans le cadre de votre application.

Prenons cet exemple: il y a peu de temps, j'ai développé quelque chose comme Google Forms. J'ai commencé avec CustomFormServiceet a fini avec CustomFormService, CustomFormRender, CustomFieldService, CustomFieldRender, CustomAnswerServiceet CustomAnswerRender. Pourquoi? Parce que cela avait du sens pour moi. Si vous travaillez avec une équipe, vous devez placer votre logique là où elle a du sens pour l'équipe.

L'avantage d'utiliser les services par rapport aux contrôleurs / modèles est que vous n'êtes pas contraint par un seul contrôleur ou un seul modèle. Vous pouvez créer autant de services que nécessaire en fonction de la conception et des besoins de votre application. Ajoutez à cela l'avantage d'appeler un service dans n'importe quelle classe de votre application.

Cela prend du temps, mais j'aimerais vous montrer comment j'ai structuré ma candidature:

app/
    controllers/
    MyCompany/
        Composers/
        Exceptions/
        Models/
        Observers/
        Sanitizers/
        ServiceProviders/
        Services/
        Validators/
    views
    (...)

J'utilise chaque dossier pour une fonction spécifique. Par exemple, le Validatorsrépertoire contient une BaseValidatorclasse responsable du traitement de la validation, basée sur le $ruleset $messagesde validateurs spécifiques (généralement un pour chaque modèle). Je pourrais aussi facilement mettre ce code dans un service, mais il me semble logique d'avoir un dossier spécifique pour cela même s'il n'est utilisé que dans le service (pour l'instant).

Je vous recommande de lire les articles suivants, car ils pourraient vous expliquer un peu mieux les choses:

Breaking the Mold de Dayle Rees (auteur de CodeBright): C'est là que j'ai mis tout cela ensemble, même si j'ai changé certaines choses pour répondre à mes besoins.

Découpler votre code dans Laravel à l'aide de référentiels et de services par Chris Goosey: Cet article explique bien ce qu'est un service et le modèle de référentiel et comment ils s'articulent.

Les laracasts ont également les référentiels simplifiés et à responsabilité unique qui sont de bonnes ressources avec des exemples pratiques (même si vous devez payer).

Luís Cruz
la source
3
bonne explication. Voici un peu où j'en suis pour le moment - dans le projet actuel, je mets ma logique commerciale dans des modèles et cela fonctionne très bien. Nous devons certainement truquer un peu SOLID, mais cela ne nous a pas encore vraiment causé d'ennuis. C'est rapide, c'est un peu sale, mais jusqu'à présent, notre projet est très maintenable car il est tellement SEC. Je suis vraiment d'accord pour m'en tenir à eux pour le moment car ils font le travail, mais dans tout projet futur, je vais probablement utiliser ce qui est standard, ce qui semble être des référentiels.
Sabrina Leggett
2
Je suis content que vous ayez trouvé un moyen qui vous convient. Faites juste attention aux hypothèses que vous faites aujourd'hui . J'ai travaillé sur un projet pendant plus de 3 ans et j'ai fini avec des contrôleurs et des modèles avec plus de 5000 lignes de code. Bonne chance pour votre projet.
Luís Cruz
aussi un peu sale mais je pensais utiliser des traits pour éviter que les modèles ne deviennent énormes. De cette façon, je peux les séparer un peu
Sabrina Leggett
Cet article s'articule bien QUAND il est logique d'utiliser des services. Dans votre exemple de formulaire, il est logique d'utiliser des services, mais il explique comment il le fait, c'est-à-dire lorsque la logique est directement liée à un modèle, il la place dans ce modèle. justinweiss.com/articles/where-do-you-put-your-code
Sabrina Leggett
J'aime vraiment l'explication. Il y a une question que je me pose: vous avez mentionné de ne pas mettre la validation dans le contrôleur, alors quel est selon vous le meilleur endroit pour faire la validation? Beaucoup suggèrent de le mettre dans la classe Request étendue (et aussi ce que nous faisons actuellement), mais que se passe-t-il si je ne veux pas seulement valider sur requête http, mais aussi sur commande artisanale, etc., est-ce vraiment un bon endroit?
kingshark
24

Je voulais poster une réponse à ma propre question. Je pourrais en parler pendant des jours, mais je vais essayer de mettre cela en ligne rapidement pour être sûr de le lire.

J'ai fini par utiliser la structure existante fournie par Laravel, ce qui signifie que j'ai conservé mes fichiers principalement en tant que modèle, vue et contrôleur. J'ai également un dossier Bibliothèques pour les composants réutilisables qui ne sont pas vraiment des modèles.

JE N'AI PAS EMBALLÉ MES MODÈLES DANS DES SERVICES / BIBLIOTHÈQUES . Toutes les raisons fournies ne m'ont pas convaincu à 100% des avantages de l'utilisation des services. Bien que je puisse me tromper, pour autant que je sache, ils entraînent simplement des tonnes de fichiers supplémentaires presque vides que je dois créer et basculer entre les deux lorsque vous travaillez avec des modèles et réduisent également vraiment l'avantage d'utiliser des modèles éloquents (en particulier lorsqu'il s'agit de récupérer des modèles. , par exemple, en utilisant la pagination, les portées, etc.).

J'ai mis la logique métier DANS LES MODÈLES et l'accès éloquent directement depuis mes contrôleurs. J'utilise un certain nombre d'approches pour m'assurer que la logique métier n'est pas contournée:

  • Accesseurs et mutateurs: Laravel a d'excellents accesseurs et mutateurs. Si je veux effectuer une action chaque fois qu'un article est déplacé du brouillon vers publié, je peux l'appeler en créant la fonction setIsPublishedAttribute et en y incluant la logique
  • Remplacer Créer / Mettre à jour, etc.: Vous pouvez toujours remplacer les méthodes Eloquent dans vos modèles pour inclure des fonctionnalités personnalisées. De cette façon, vous pouvez appeler des fonctionnalités sur n'importe quelle opération CRUD. Edit: Je pense qu'il y a un bug avec le remplacement de create dans les nouvelles versions de Laravel (donc j'utilise les événements maintenant enregistrés au démarrage)
  • Validation: J'accroche ma validation de la même manière, par exemple, j'exécute la validation en remplaçant les fonctions CRUD et également les accesseurs / mutateurs si nécessaire. Voir Esensi ou dwightwatson / validating pour plus d'informations.
  • Méthodes magiques: j'utilise les méthodes __get et __set de mes modèles pour m'accrocher aux fonctionnalités le cas échéant
  • Étendre Eloquent: S'il y a une action que vous souhaitez entreprendre sur toutes les mises à jour / créer, vous pouvez même étendre Eloquent et l'appliquer à plusieurs modèles.
  • Événements: C'est un endroit simple et généralement convenu pour le faire également. Le plus gros inconvénient des événements, je pense, est que les exceptions sont difficiles à retracer (ce n'est peut-être pas le nouveau cas avec le nouveau système d'événements de Laravel). J'aime aussi regrouper mes événements par ce qu'ils font plutôt que par le moment où ils sont appelés ... par exemple, avoir un abonné MailSender qui écoute les événements qui envoient du courrier.
  • Ajout d'événements Pivot / BelongsToMany: L'une des choses avec lesquelles j'ai lutté le plus longtemps était de savoir comment attacher un comportement à la modification des relations appartToMany. Par exemple, effectuer une action chaque fois qu'un utilisateur rejoint un groupe. J'ai presque fini de peaufiner une bibliothèque personnalisée pour cela. Je ne l'ai pas encore publié mais il est fonctionnel! Je vais essayer de publier un lien bientôt. EDIT J'ai fini par transformer tous mes pivots en modèles normaux et ma vie a été tellement plus facile ...

Répondre aux préoccupations des gens concernant l'utilisation de modèles:

  • Organisation: Oui, si vous incluez plus de logique dans les modèles, ils peuvent être plus longs, mais en général, j'ai trouvé que 75% de mes modèles sont encore assez petits. Si j'ai choisi d'organiser les plus grands, je peux le faire en utilisant des traits (par exemple, créer un dossier pour le modèle avec quelques fichiers supplémentaires comme PostScopes, PostAccessors, PostValidation, etc. selon les besoins). Je sais que ce n'est pas nécessairement à quoi servent les traits, mais ce système fonctionne sans problème.

Note supplémentaire: J'ai l'impression que le fait d'emballer vos modèles dans des services, c'est comme avoir un couteau suisse, avec beaucoup d'outils, et construire un autre couteau autour de lui qui fait essentiellement la même chose? Ouais, parfois vous voudrez peut-être scotcher une lame ou vous assurer que deux lames sont utilisées ensemble ... mais il existe généralement d'autres façons de le faire ...

QUAND UTILISER LES SERVICES : Cet article présente très bien de GRANDS exemples d'utilisation des services ( indice: ce n'est pas très souvent ). Il dit essentiellement que lorsque votre objet utilise plusieurs modèles ou modèles à des parties étranges de leur cycle de vie, cela a du sens. http://www.justinweiss.com/articles/where-do-you-put-your-code/

Sabrina Leggett
la source
2
Pensées intéressantes et valables. Mais je suis curieux - comment testez-vous en bloc votre logique métier si elle est liée à des modèles liés à Eloquent, qui est lié à la base de données?
JustAMartin
code.tutsplus.com/tutorials / ... ou vous pouvez utiliser des événements comme je l'ai dit si vous voulez le décomposer davantage
Sabrina Leggett
1
@JustAMartin êtes-vous sûr que vous ne pouvez pas simplement utiliser la base de données dans vos tests unitaires? Quelle est la raison de ne pas le faire? Beaucoup de gens conviennent qu'il est souvent acceptable d'utiliser la base de données dans les tests unitaires. (y compris Martin Fowler, martinfowler.com/bliki/UnitTest.html : "Je ne considère pas l'utilisation de doubles pour les ressources externes comme une règle absolue. Si parler à la ressource est assez stable et rapide pour vous, il n'y a aucune raison de ne pas le faire dans vos tests unitaires ")
Alex P.
@ AlexP11223 Oui, c'est logique. J'ai essayé d'intégrer SQLite comme base de données de test et en général cela s'est bien passé, bien que SQLite ait quelques limitations sérieuses qui doivent être prises en compte dans les migrations Laravel et les requêtes personnalisées (le cas échéant). Bien sûr, ce ne sont pas strictement des tests unitaires mais des tests fonctionnels, mais c'est encore plus efficace de cette façon. Néanmoins, si vous souhaitez tester votre modèle de manière totalement isolée (en tant que test unitaire strict), cela peut nécessiter une quantité notable de code supplémentaire (simulations, etc.).
JustAMartin
22

Ce que j'utilise pour créer la logique entre les contrôleurs et les modèles est de créer une couche de service . En gros, voici mon flux pour toute action au sein de mon application:

  1. Le contrôleur récupère l'action demandée par l'utilisateur et envoie les paramètres et délègue tout à une classe de service.
  2. La classe de service fait toute la logique liée à l'opération: validation des entrées, journalisation des événements, opérations de base de données, etc ...
  3. Le modèle contient des informations sur les champs, la transformation des données et les définitions des validations d'attributs.

Voici comment je fais:

Voici la méthode d'un contrôleur pour créer quelque chose:

public function processCreateCongregation()
{
    // Get input data.
    $congregation                 = new Congregation;
    $congregation->name           = Input::get('name');
    $congregation->address        = Input::get('address');
    $congregation->pm_day_of_week = Input::get('pm_day_of_week');
    $pmHours                      = Input::get('pm_datetime_hours');
    $pmMinutes                    = Input::get('pm_datetime_minutes');
    $congregation->pm_datetime    = Carbon::createFromTime($pmHours, $pmMinutes, 0);

    // Delegates actual operation to service.
    try
    {
        CongregationService::createCongregation($congregation);
        $this->success(trans('messages.congregationCreated'));
        return Redirect::route('congregations.list');
    }
    catch (ValidationException $e)
    {
        // Catch validation errors thrown by service operation.
        return Redirect::route('congregations.create')
            ->withInput(Input::all())
            ->withErrors($e->getValidator());
    }
    catch (Exception $e)
    {
        // Catch any unexpected exception.
        return $this->unexpected($e);
    }
}

C'est la classe de service qui fait la logique liée à l'opération:

public static function createCongregation(Congregation $congregation)
{
    // Log the operation.
    Log::info('Create congregation.', compact('congregation'));

    // Validate data.
    $validator = $congregation->getValidator();

    if ($validator->fails())
    {
        throw new ValidationException($validator);
    }

    // Save to the database.
    $congregation->created_by = Auth::user()->id;
    $congregation->updated_by = Auth::user()->id;

    $congregation->save();
}

Et voici mon modèle:

class Congregation extends Eloquent
{
    protected $table = 'congregations';

    public function getValidator()
    {
        $data = array(
            'name' => $this->name,
            'address' => $this->address,
            'pm_day_of_week' => $this->pm_day_of_week,
            'pm_datetime' => $this->pm_datetime,
        );

        $rules = array(
            'name' => ['required', 'unique:congregations'],
            'address' => ['required'],
            'pm_day_of_week' => ['required', 'integer', 'between:0,6'],
            'pm_datetime' => ['required', 'regex:/([01]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5][0-9]/'],
        );

        return Validator::make($data, $rules);
    }

    public function getDates()
    {
        return array_merge_recursive(parent::getDates(), array(
            'pm_datetime',
            'cbs_datetime',
        ));
    }
}

Pour plus d'informations sur cette façon dont j'utilise pour organiser mon code pour une application Laravel: https://github.com/rmariuzzo/Pitimi

Rubens Mariuzzo
la source
Il semble que les services sont ce que j'ai appelé des bibliothèques dans mon article. Je pense que c'est mieux que les référentiels si vous n'avez pas besoin d'utiliser plusieurs ORMS, mais le problème est que vous devez migrer l'ensemble de votre projet (ce que vous n'avez pas à faire avec des événements), et il semble que cela finit en quelque sorte par refléter la structure du modèle, vous n'avez donc que tous ces fichiers supplémentaires. Pourquoi ne pas simplement l'inclure dans les modèles? Au moins de cette façon, vous n'avez pas les fichiers supplémentaires.
Sabrina Leggett
C'est une question intéressante @SabrinaGelbart, on m'a appris à laisser les modèles représenter les entités de la base de données et à ne contenir aucune logique. C'est la raison pour laquelle j'ai créé ces fichiers supplémentaires appelés services: pour contenir toute la logique et toutes les opérations supplémentaires. Je ne sais pas quelle est la signification des événements que vous avez décrits auparavant, mais je pense qu'avec les services et en utilisant les événements de Laravel, nous pouvons créer toutes les méthodes de services pour déclencher des événements au début et à la fin. De cette façon, tout événement peut être complètement découplé de la logique. Qu'est-ce que tu penses?
Rubens Mariuzzo
On m'a appris que trop sur les modèles ... serait bien d'obtenir une bonne explication pour pourquoi (peut-être des problèmes de dépendance)?
Sabrina Leggett
J'aime cette approche! J'ai cherché sur Internet pour avoir une idée de la façon dont je devrais gérer la logique du modèle, j'ai examiné les référentiels mais cela me semblait trop compliqué et inutile pour un peu d'utilisation. Les services sont une bonne idée. Ma question est après la création d'un dossier de services dans le dossier de l'application, devez-vous l'inclure dans bootstrap / start.php ou n'importe où pour démarrer parce que j'ai regardé votre git ne le trouve pas? @RubensMariuzzo. Est-il automatiquement disponible via l'application? donc nous pouvons simplement utiliser CongregationService :: getCongregations (); ??
Oguzhan
1
Si tout ce que vous faites est un, $congregation->save();vous n'aurez peut-être pas besoin de référentiels. Cependant, vous pouvez voir vos besoins d'accès aux données augmenter avec le temps. Vous pouvez commencer à avoir des besoins pour $congregation->destroyByUser()ou $congregationUsers->findByName($arrayOfSelectedFields);et ainsi de suite. Pourquoi ne pas dissocier vos services des besoins d'accès aux données. Laissez le reste de votre application travailler avec des objets / tableaux renvoyés par les dépôts, et gérez simplement la manipulation / le formatage / etc ... Vos dépôts grandiront (mais les diviser en différents fichiers, en fin de compte, la complexité d'un projet doit résider quelque part).
prograhammer
12

À mon avis, Laravel a déjà de nombreuses options pour stocker votre logique métier.

Réponse courte:

  • Utilisez les Requestobjets de laravel pour valider automatiquement votre entrée, puis persistez les données dans la requête (créez le modèle). Étant donné que toutes les entrées des utilisateurs sont directement disponibles dans la demande, je pense qu'il est logique de l'exécuter ici.
  • Utilisez les Jobobjets de laravel pour effectuer des tâches qui nécessitent des composants individuels, puis envoyez-les simplement. Je pense que cela Jobenglobe les classes de service. Ils exécutent une tâche, telle que la logique métier.

Réponse longue (heu):

Utiliser les référentiels lorsque cela est nécessaire: les référentiels sont voués à être surchargés et, la plupart du temps, sont simplement utilisés comme un accessormodèle. J'ai l'impression qu'ils ont certainement une certaine utilité, mais à moins que vous ne développiez une application massive qui nécessite autant de flexibilité pour que vous puissiez abandonner complètement laravel, restez à l'écart des dépôts. Vous vous remercierez plus tard et votre code sera beaucoup plus simple.

Demandez-vous s'il y a une possibilité que vous changiez les frameworks PHP ou un type de base de données que laravel ne prend pas en charge.

Si votre réponse est "Probablement pas", n'implémentez pas le modèle de référentiel.

En plus de ce qui précède, veuillez ne pas gifler un motif sur un superbe ORM comme Eloquent. Vous ajoutez simplement de la complexité qui n'est pas requise et qui ne vous sera d'aucune utilité.

Utilisez les services avec parcimonie: les classes de service ne sont pour moi qu'un endroit où stocker la logique métier pour effectuer une tâche spécifique avec ses dépendances données. Laravel les a prêts à l'emploi, appelés «Jobs», et ils ont beaucoup plus de flexibilité qu'une classe de service personnalisée.

J'ai l'impression que Laravel a une solution complète pour le MVCproblème de logique. C'est juste une question ou une organisation.

Exemple:

Demande :

namespace App\Http\Requests;

use App\Post;
use App\Jobs\PostNotifier;
use App\Events\PostWasCreated;
use App\Http\Requests\Request;

class PostRequest extends Request
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title'       => 'required',
            'description' => 'required'
        ];
    }

    /**
     * Save the post.
     *
     * @param Post $post
     *
     * @return bool
     */
    public function persist(Post $post)
    {
        if (!$post->exists) {
            // If the post doesn't exist, we'll assign the
            // post as created by the current user.
            $post->user_id = auth()->id();
        }

        $post->title = $this->title;
        $post->description = $this->description;

        // Perform other tasks, maybe fire an event, dispatch a job.

        if ($post->save()) {
            // Maybe we'll fire an event here that we can catch somewhere else that
            // needs to know when a post was created.
            event(new PostWasCreated($post));

            // Maybe we'll notify some users of the new post as well.
            dispatch(new PostNotifier($post));

            return true;
        }

        return false;
    }
}

Contrôleur :

namespace App\Http\Controllers;

use App\Post;
use App\Http\Requests\PostRequest;

class PostController extends Controller
{

   /**
    * Creates a new post.
    *
    * @return string
    */
    public function store(PostRequest $request)
    {
        if ($request->persist(new Post())) {
            flash()->success('Successfully created new post!');
        } else {
            flash()->error('There was an issue creating a post. Please try again.');
        }

        return redirect()->back();
    }

   /**
    * Updates a post.
    *
    * @return string
    */
    public function update(PostRequest $request, $id)
    {
        $post = Post::findOrFail($id);

        if ($request->persist($post)) {
            flash()->success('Successfully updated post!');
        } else {
            flash()->error('There was an issue updating this post. Please try again.');
        }

        return redirect()->back();
    }
}

Dans l'exemple ci-dessus, l'entrée de la requête est automatiquement validée, et tout ce que nous avons à faire est d'appeler la méthode persist et de transmettre un nouveau message. Je pense que la lisibilité et la maintenabilité devraient toujours l'emporter sur les modèles de conception complexes et inutiles.

Vous pouvez ensuite utiliser exactement la même méthode persistante pour mettre à jour les publications, car nous pouvons vérifier si la publication existe déjà ou non et effectuer une logique alternée si nécessaire.

Steve Bauman
la source
mais - les emplois ne sont-ils pas «censés» être mis en file d'attente? Parfois, nous voulons probablement qu'il soit mis en file d'attente, mais pas tout le temps. Pourquoi ne pas utiliser les commandes à la place? Que faire si vous souhaitez écrire une logique métier qui peut être exécutée en tant que commande, événement ou mise en file d'attente?
Sabrina Leggett
1
Les travaux n'ont pas besoin d'être mis en file d'attente. Vous le spécifiez en implémentant l'interface sur le travail ShouldQueuefourni par Laravel. Si vous souhaitez écrire la logique métier dans une commande ou un événement, lancez simplement le travail à l'intérieur de ces événements / commandes. Les emplois Laravels sont extrêmement flexibles, mais en fin de compte, ce ne sont que des classes de service simples.
Steve Bauman