• Stars
    star
    283
  • Rank 146,066 (Top 3 %)
  • Language
    PHP
  • License
    MIT License
  • Created over 10 years ago
  • Updated almost 10 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Easily leverage commands and domain events in your Laravel projects.

Laravel Commander

This package gives you an easy way to leverage commands and domain events in your Laravel projects.

Laravel 5 users: this package is no longer required, as the framework includes its own flexible command bus out of the box.

Installation

Per usual, install Commander through Composer.

"require": {
    "laracasts/commander": "~1.0"
}

Next, update app/config/app.php to include a reference to this package's service provider in the providers array.

'providers' => [
    'Laracasts\Commander\CommanderServiceProvider'
]

Usage

Easily, the most important piece of advice I can offer is to keep in mind that this approach isn't for everything. If you're building a simple CRUD app that does not have much business logic, then you likely don't need this. Still want to move ahead? Okay - onward!

The Goal

Imagine that you're building an app for advertising job listings. Now, when an employer posts a new job listing, a number of things need to happen, right? Well, don't put all that stuff into your controller! Instead, let's leverage commands, handlers, and domain events to clean up our code.

The Controller

To begin, we can inject this package's CommanderTrait into your controller (or a BaseController, if you wish). This will give you a couple helper methods to manage the process of passing commands to the command bus.

<?php

use Laracasts\Commander\CommanderTrait;

class JobsController extends \BaseController {

	use CommanderTrait;

	/**
	 * Publish the new job listing.
	 *
	 * @return Response
	 */
	public function store()
	{

	}

}

Good? Next, we'll represent this "instruction" (to post a job listing) as a command. This will be nothing more than a simple DTO.

<?php

use Laracasts\Commander\CommanderTrait;
use Acme\Jobs\PostJobListingCommand;

class JobsController extends \BaseController {

	use CommanderTrait;

	/**
	 * Post the new job listing.
	 *
	 * @return Response
	 */
	public function store()
	{
        $this->execute(PostJobListingCommand::class);

		return Redirect::home();
	}

Notice how we are representing the user's instruction (or command) as a readable class: PostJobListingCommand. The execute method will expect the command's class path, as a string. Above, we're using the helpful PostJobListingCommand::class to fetch this. Alternatively, you could manually write out the path as a string.

The Command DTO

Pretty simply, huh? We make a command to represent the instruction, and then we throw that command into a command bus. Here's what that command might look like:

<?php namespace Acme\Jobs;

class PostJobListingCommand {

    public $title;

    public $description;

    public function __construct($title, $description)
    {
        $this->title = $title;
        $this->description = $description;
    }

}

When you call the execute method on the CommanderTrait, it will automatically map the data from Input::all() to your command. You won't need to worry about doing that manually.

So what exactly does the command bus do? Think of it as a simple utility that will translate this command into an associated handler class that will, well, handle the command! In this case, that means delegating as needed to post the new job listing.

By default, the command bus will do a quick search and replace on the name of the command class to figure out which handler class to resolve out of the IoC container. As such:

  • PostJobListingCommand => PostJobListingCommandHandler
  • ArchiveJobCommand => ArchiveJobCommandHandler

Make sense? Good. Keep in mind, though, that if you prefer a different naming convention, you can override the defaults. See below.

Decorating the Command Bus

There may be times when you want to decorate the command bus to first perform some kind of action...maybe you need to first sanitize some data. Well, that's easy. First, create a class that implements the Laracasts\Commander\CommandBus contract...

<?php namespace Acme\Jobs;

use Laracasts\Commander\CommandBus;

class JobSanitizer implements CommandBus {

    public function execute($command)
    {
       // sanitize the job data
    }

}

...and now reference this class, when you execute the command in your controller.

$this->execute(PostJobListingCommand::class, null, [
    'JobSanitizer'
]);

And that's it! Now, you have a hook to sanitize the command/data before it's passed on to the handler class. On that note...

The Handler Class

Let's create our first handler class now:

<?php namespace Acme\Jobs;

use Laracasts\Commander\CommandHandler;
use Laracasts\Commander\Events\DispatchableTrait;

class PostJobListingCommandHandler implements CommandHandler {

    use DispatchableTrait;

    public function handle($command)
    {
        $job = Job::post($command->title, $command->description);

        $this->dispatchEventsFor($job);

        return $job;
    }

}

For this demo, our handler is fairly simple. In real-life, more would be going on here. Notice that dispatchEventsFor method? This will handle the process of firing all queued events for your entity. This way, other parts of your app may listen for when a job has been published, and respond accordingly.

Raising an Event

Here's a quick and dirty example of what that Job model might look like:

<?php namespace Acme\Jobs;

use Laracasts\Commander\Events\EventGenerator;
use Acme\Jobs\Events\JobWasPublished;

class Job extends \Eloquent {

    use EventGenerator;

    protected $fillable = ['title', 'description'];

    public static function post($title, $description)
    {
        // We're ignoring persistence for this demo
        $job = new static(compact('title', 'description'));

        $job->raise(new JobWasPublished($job));

        return $job;
    }
}

Pay close to attention to where we raise that event.

$job->raise(new JobWasPublished($job));

Once again, this JobWasPublished object is nothing more than a simple transport object.

<?php namespace Acme\Jobs\Events;

use Acme\Jobs\Job;

class JobWasPublished {

    public $job;

    public function __construct(Job $job) /* or pass in just the relevant fields */
    {
        $this->job = $job;
    }

}

Also, the raise method is available through that EventGenerator trait. It simply stores the event in an array.

Event Listeners

Now, because the handler class dispatched all events, that means you can register any number of listeners. The event name to listen for follows, once again, a simple convention:

  • Path\To\Raised\Event => Path.To.Raised.Event

So, essentially, we replace slashes with periods to make it appear just a bit more object-oriented. So, if we raise:

  • Acme\Jobs\Events\JobWasPublished

Then, the event to listen for will be:

  • Acme.Jobs.Events.JobWasPublished

Let's register a basic event listener to fire off an email.

Event::listen('Acme.Jobs.Events.JobWasPublished', function($event)
{
    var_dump('Send a notification email to the job creator.');
});

Remember: you can register multiple listeners for the same event. Maybe you also need to do something related to reporting when a job is published. Well, add a new event listener!

Now, this example above uses a simple closure. If you want, you could take a more "catch-all" approach, which this package can help with.

First, let's setup an EmailNotifier class that will be given a chance to handle all fired events for our app.

Event::listen('Acme.*', 'Acme\Listeners\EmailNotifier');

So, now, any time that you raise an event in the Acme namespace, once dispached, the EmailNotifier class' handle method will fire. Naturally, though, we don't need to respond to every event! Just a few. Well, once again, we can follow a simple method naming convention to respond to only the events that we are interested in.

The JobWasPublished event class will look for a whenJobWasPublished method on your event listener. If it exists, it will call it. Otherwise, it'll simply continue on. That means our EmailNotifier class might look like so:

<?php namespace Acme\Listeners;

use Laracasts\Commander\Events\EventListener;
use Acme\Jobs\Events\JobWasPublished;

class EmailNotifier extends EventListener {

    public function whenJobWasPublished(JobWasPublished $event)
    {
        var_dump('send an email');
    }

}

Because this class extends EventListener, that parent class will manage all the details of determining if whenJobWasPublished should be called.

File Generation

You'll likely find yourself manually creating lots and lots of commands and handler classes. Instead, use the Artisan command that is included with this package! Simply run:

php artisan commander:generate Acme/Bar/SubscribeUserCommand

This will generate both SubscribeUserCommand and a SubscribeUserCommandHandler classes. By default, it will look for that "Acme" directory within "app/". If your base domain directory is somewhere else, pass the --base="src".

The Command

<?php namespace Acme\Bar;

class SubscribeUserCommand {

    /**
     * Constructor
     */
    public function __construct()
    {
    }

}

The Handler

<?php namespace Acme\Bar;

use Laracasts\Commander\CommandHandler;

class SubscribeUserCommandHandler implements CommandHandler {

    /**
     * Handle the command.
     *
     * @param object $command
     * @return void
     */
    public function handle($command)
    {

    }

}

Or, if you also want boilerplate for the properties, you can do that as well.

php artisan commander:generate Acme/Bar/SubscribeUserCommand --properties="first, last"

When you add the --properties flag, the handle class will remain the same, however, the command, itself, will be scaffolded, like so:

<?php namespace Acme\Bar;

class SubscribeUserCommand {

    /**
     * @var string
     */
    public $first;

    /**
     * @var string
     */
    public $last;

    /**
     * Constructor
     *
     * @param string first
     * @param string last
     */
    public function __construct($first, $last)
    {
        $this->first = $first;
        $this->last = $last;
    }

}

Nifty, ay? That'll save you a lot of time, so remember to use it.

When calling this command, use forward slashes for your class path: Acme/Bar/MyCommand. If you'd rather use backslashes, you'll need to wrap it in quotes.

That Does It!

This can be complicated stuff to read. Be sure to check out the Commands and Domain Events series on Laracasts to learn more about this stuff.

More Repositories

1

flash

Easy flash notifications
PHP
2,637
star
2

Laravel-5-Generators-Extended

This package extends the core file generators that are included with Laravel 5
PHP
2,447
star
3

PHP-Vars-To-Js-Transformer

Transform PHP data to JavaScript.
PHP
2,206
star
4

Lets-Build-a-Forum-in-Laravel

http://laracasts.com/series/lets-build-a-forum-with-laravel
JavaScript
912
star
5

Presenter

Easy view presenters in your apps.
PHP
864
star
6

cypress

Laravel Cypress Integration
PHP
568
star
7

Integrated

Simple, intuitive integration testing with PHPUnit.
PHP
478
star
8

TestDummy

Easy factories for PHP integration testing.
PHP
461
star
9

Vue-Forms

https://laracasts.com/series/learn-vue-2-step-by-step/episodes/19
PHP
402
star
10

Behat-Laravel-Extension

Laravel extension for Behat functional testing.
PHP
261
star
11

matryoshka

Russian Doll Caching in Laravel
PHP
237
star
12

birdboard

Birdboard Series Code
PHP
236
star
13

Tweety

The final project from Laravel From Scratch.
PHP
178
star
14

The-PHP-Practitioner-Full-Source-Code

PHP
176
star
15

Laravel-From-Scratch-HTML-CSS

The HTML and CSS for the blog design for Laravel From Scratch
HTML
158
star
16

laravel-5-roles-and-permissions-demo

https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/16
PHP
153
star
17

Validation

Easy form validation.
PHP
150
star
18

larabook

Larabook Series
PHP
142
star
19

PHP-For-Beginners-Series

PHP
119
star
20

laravel-vue-spa

PHP
110
star
21

Dedicated-Query-String-Filtering

https://laracasts.com/series/eloquent-techniques/episodes/4
PHP
94
star
22

Code-Katas-in-PHP

Screencasts of various code kata challenges in HP.
PHP
88
star
23

Email-Verification-In-Laravel

PHP
83
star
24

URL-Shortener

For demo at Laracasts.com
PHP
82
star
25

The-PHP-Practitioner-Episode-16

PHP
70
star
26

Eloquent-Outside-of-Laravel

In this lesson, you'll learn how to use Eloquent in a simple vanilla PHP environment. Luckily, it's pretty easy! For smaller projects that don't require the overhead of a full-stack framework, this method can be a great choice!
PHP
69
star
27

eloquent-performance-patterns

PHP
62
star
28

Build-An-Activity-Feed-in-Laravel

https://laracasts.com/lessons/build-an-activity-feed-in-laravel
JavaScript
58
star
29

Laravel-Redis-and-Socket.io

PHP
56
star
30

Laravel-OAuth-and-Socialite

PHP
55
star
31

Laravel-and-Vue

JavaScript
54
star
32

Email-Only-Authentication-With-Laravel

https://laracasts.com/series/laravel-authentication-techniques/episodes/1
PHP
54
star
33

transcriptions

Load and parse VTT files.
PHP
53
star
34

Laravel-and-Angular-Goodness

PHP
53
star
35

testingvue

http://testingvue.com
PHP
50
star
36

simple-di-container

PHP
48
star
37

Vue-SPA-Essentials-Routing

JavaScript
45
star
38

Mass-User-Settings

PHP
40
star
39

Users-and-Roles-in-Laravel

What if you want to assign a user to a particular role? For example, some of them might be classified as members, while others could be administrators. How might we allow for such things?
PHP
40
star
40

Nested-Comments

PHP
38
star
41

Gilded-Rose-Kata-in-PHP

PHP
38
star
42

Laravel-Elixir-Vueify-Setup

JavaScript
38
star
43

Blade-Component-Examples

PHP
37
star
44

In-Stock-Tracker

PHP
34
star
45

roles-and-abilities

PHP
33
star
46

socket-io-chat-example-app

HTML
32
star
47

Reports-and-Graphs

Using ChartJS to build line graphs with Laravel.
PHP
32
star
48

Accept-Payments-With-Stripe

https://laracasts.com/series/how-to-accept-payments-with-stripe
PHP
31
star
49

The-Vast-World-of-Vuejs

Lesson 1 Source
HTML
30
star
50

How-Do-I-Create-A-Star-Rating-System

https://laracasts.com/series/how-do-i/episodes/22
PHP
29
star
51

Learn-Flexbox-Through-Examples

HTML
29
star
52

JS-Component-Playground

https://laracasts.com/series/practical-vue-components
PHP
28
star
53

The-PHP-Practitioner-Episode-20

https://laracasts.com/series/php-for-beginners/episodes/20
PHP
27
star
54

GitHub-Authentication-With-Laravel

There are a number of excellent OAuth packages available, however, this is Laracasts, and we want to know how to, not reinvent the wheel, but rebuild the wheel! With that in mind, in this lesson, let's review the general process of how to allow your users to login to your application, using GitHub (or any provider).
PHP
26
star
55

build_a_forum_with_laravel_2023

Vue
24
star
56

Building-User-Profiles

In this lesson, we'll review the process of adding user profiles to an application. In the process, we'll review everything from database design, to migrations, to security, to validation. Let's get going!
PHP
24
star
57

laravel-api-master-class

PHP
24
star
58

Vue-and-Laravel-Workflow

JavaScript
24
star
59

Testing-Vue

JavaScript
23
star
60

Behat-Laravel-Extension-Example-App

Quick example app to demonstrate setting up Behat with Mink and Laravel extension
CSS
23
star
61

Pjax-and-Laravel

PHP
22
star
62

adding-passkeys-to-your-laravel-app

The repository for Luke's "Adding Passkeys to Your Laravel App" course.
PHP
21
star
63

Billing-With-Stripe

PHP
21
star
64

Build-Command-line-Apps

PHP
21
star
65

Widget

Simple view widgets.
PHP
20
star
66

todomvc-alpine

A quick TodoMVC implementation with Alpine.js
HTML
20
star
67

laravel-preset

The Laracasts default Laravel preset.
PHP
19
star
68

Russian-Doll-Caching-in-Laravel

https://laracasts.com/series/russian-doll-caching-in-laravel
PHP
19
star
69

Authentication-With-GitHub

https://laracasts.com/series/laravel-authentication-techniques/episodes/2
PHP
18
star
70

Pusher-Lesson

So you run a forum, and need some way to notify readers, if new replies have been left on a thread since their last page load. Well, how the heck do we do that? How do we say, "If a new reply is saved to the server, instantly update all viewers who might be reading the associated forum thread"? Well, Pusher can make these sorts of tasks a cinch! You'll love it.
JavaScript
18
star
71

Build-Artisan-Commands-With-TDD

In this fun two-part series, we'll use TDD to build a helpful Artisan command. Along the way, we'll leverage both Codeception (for the end-to-end tests) and PHPSpec (for unit tests) to drive our code. There's lots to learn, so let's get going.
PHP
18
star
72

PHP-Testing-Jargon

PHP
17
star
73

Document-Adjustments-Demo

https://laracasts.com/series/eloquent-techniques/episodes/3
PHP
17
star
74

form-objects-lesson

https://laracasts.com/series/whipping-monstrous-code-into-shape/episodes/1
PHP
17
star
75

Reusable-Repositories

So you're using repositories, but have found the process of constantly reimplementing common methods to be cumbersome?
PHP
17
star
76

Bash-Bootstraps

Often, it can prove helpful to write simple Bash scripts to bootstrap your new applications. The only question isโ€ฆhow do we do that? Let me show you!
Shell
17
star
77

VideoJS-Events-and-AJAX

PHP
16
star
78

Snippets-Project

https://laracasts.com/series/how-do-i/episodes/13
PHP
16
star
79

Transactional-Emails-in-Laravel-with-Campaign-Monitor

PHP
16
star
80

assets-website

PHP
15
star
81

Design-a-Fluent-API-with-TDD

https://laracasts.com/series/phpunit-testing-in-laravel/episodes/11
PHP
15
star
82

Help-Me-Understand-When-to-Use-Polymorphic-Relations

PHP
15
star
83

PHPSpec-Rocks

PHPSpec is the best test framework that you likely haven't used. That just might change after watching this video, though. It's excellent!
PHP
15
star
84

PHPSpec-Laravel-and-Refactoring

Any confusion that surrounds PHPSpec likely stems from a misunderstanding of what the framework is for. There are multiple styles and forms of testing; PHPSpec is not meant to fill all of those checkboxes. Let's talk about that in this lesson, while using BDD to build a class within a Laravel app.
PHP
14
star
85

Crazy-Simple-Pagination

PHP
13
star
86

Flexible-Flash-Messages

You'll frequently find yourself in the position of needing to notify your users, in response to some particular action. In this lesson, we'll uses tests to drive flexible flash messaging.
PHP
13
star
87

the-specification-pattern-in-php

PHP
13
star
88

laravel-workflow-for-swapping-vue-components

JavaScript
13
star
89

Bulk-Email-Notifications

In this lesson, we'll finish up our Mailchimp email notifications mini-project. Specifically, we'll focus on putting all the pieces that we built in the previous lesson together. This will include the creation of a UsersController, as well as a custom Artisan command to manually notify lesson subscribers.
PHP
13
star
90

Dynamic-Graphs-Lesson

https://laracasts.com/series/charting-and-you/episodes/8
JavaScript
13
star
91

Form-Validation-Simplified

Form validation is an interesting thing. It's trivial to implement, yet everyone tackles it differently. In this lesson, I'd like to show you my current approach (as of April, 2014) for handling this incredibly common task.
PHP
12
star
92

Laracasts-Docs

https://laracasts.com/series/how-to-read-code/episodes/6
PHP
12
star
93

How-Do-I-Dry-Up-My-Forms

PHP
12
star
94

Laravel-4.1-Password-Resets

PHP
12
star
95

Getting-Jiggy-With-Adapters

An adapter is one of the easier design patterns to learn. The reason why is because you use them in the real world all the time! In this lesson, let's review a handful of examples to figure out how it all works.
PHP
12
star
96

exploring-laravel-reverb

The source code for Luke's "Exploring Laravel Reverb" Larabit
PHP
12
star
97

6-html-tags

6 New'ish HTML Tags You Can Use Right Now
HTML
11
star
98

gilded-rose-with-phpunit

Original Kata and Description: https://github.com/notmyself/GildedRose
PHP
10
star
99

Hands-On-Community-Contributions

https://laracasts.com/series/hands-on-community-contributions/episodes/13
PHP
10
star
100

Small-Objects-Are-a-Good-Thing-and-Other-Refactoring-Lessons

How many times have you written (or come across) a controller method that's dozens of lines long. Surely, there are better ways to structure our code, right? Of course. In this lesson, let's learn about everything from security, to small objects, to events.
PHP
10
star