The Series – Table of Contents
First of all, even though it’s already been done earlier in the series, let us recap what Spark is. And by that, let us not repeat what features it ships, but what it is. Spark is a package that provides scaffolding, meaning it lays out code for you, code that you are largely meant to alter to fit your needs. So of course, to do this, Spark is focused on extendability and ease of use throughout. Throughout this post, I will go through some of the features in the backend that make Spark stand out from the crowd.
Okay, so I’ll admit the first point isn’t about the backend at all, but it is a point I do want to make regardless. Much of what Spark is is an attempt to provide as much as possible out of the box, and trying to have you change as little as possible in the backend. Spark wants you to stop wasting time on building the billing, authentication, team structure and more. Hence, Spark’s primary focus is not to provide an easy way to modify it. Spark’s primary focus is providing something you barely have to change at all.
Alright, so let’s get to the backend. I’m gonna start with going over the public parts of Spark, the documented and accessed ones, to then continue to components in Spark’s internals.
This allows us to, through a closure, define another way of retrieving the current user. But, since I mentioned interactions earlier, I bet you’re curious to know what they are, yeah?
swap is the primary method that should be used when you wish to replace a part of the backend with an alternative. The way
swap works is by taking a
class@method string, and a closure to provide an implementation. This allows you to replace any of the existing interactions with your own implementation. Now, before we have a look at what interactions really are, let me show you the example shown in the documentation for
swap, which describes swapping the implementation of retrieving the current user, but the same would be done for any other methods in the User and Team repositories.
Interactions and Repositories
Interactions are the basis for everything in Spark. In fact, it is what makes Spark so easily extendable and swappable, once you understand it. This will just be a brief overview of the concept of interactions and how they are used. I will go over the details in my next post.
At its very basic, an interaction is just a class with a handler. An interaction can be something like “create a user”, “retrieve a user”, “start a subscription”, or any other way in which you interact with Spark.
To best understand interactions, it is most helpful to follow the flow of an application. Therefore, I will start in the
TeamController (if you have Spark, do follow along in the source, I’ll only post minor snippets here). For now, we’re going to look at the
all method, to retrieve all teams. Now, that method is very very short, all it does is one method call:
At first glance, this just looks like an over-complication for calling the
forUser method on the
TeamRepository. However, the
interact method is fundamental to spark, as it is this which allows
swap to function as it does. When interact is called, it will first check in an array on the
Spark object if there is a replacement for it (which is detected by checking for the same name, and you can pass either just a class name and the fully qualified (namespaced) class name). When
swap is called, all that happens is that your implementation is added to the aforementioned array. This is one of the things I will focus more on in my next article.
As a consequence, this actually means you can use interactions in your own code if you want to (and I will also go through exactly why this is either a great or a terrible idea in the next one).
Now, supposing you didn’t swap out the implementation of
forUser, we simply end up in the repository and retrieve the teams to which the current user has access.
But that’s just a repository, you said it was a class with a handler!
Yes, that’s right, and in this case, retrieving the teams is an interaction. However, This might still feel like an over-complication, as we could surely just rebind the implementation in the container to use our
TeamRepository which extends the Spark one. Well, first of all, that’s more tedious. However, secondly, Spark has what I have mentioned before: interaction classes. To see an example of this, let us venture to the
RegisterController@register method. That method has the following lines:
Auth::login($user = Spark::interact( Register::class, [$request] ));
Now, you can see we have no
@method syntax at all, and it appears
Register is a class that will somehow handle the registration of the user and then return the user.
If you open the
Laravel\Spark\Interactions\Auth\Register class, you will find one single public method, named
handle. The name
handle is just a standard which, when followed, means you don’t have to specify which method to call, because it’s the default. In here, you can see there is actually a bunch of code pertaining to the registration and subscription of a user (it actually calls a second,
Subscribe interaction from within the
Register interaction). Interaction classes such as this one is where most of the logic of Spark resides. This is where you will find the code to register users, check plan eligibility, send support emails, and update profile pictures, to take a few examples.
Fundamentally, what interactions do differently is that they look at the flow of action, rather than thinking about the application in terms of the controller action. This is a philosophy I’m a huge fan of (and am even working on a framework that takes this view further).
Aside from interactions, Spark also uses events a fair bit. An event-driven application is something that goes hand in hand with interactions, as it too focuses on what is happening in our application. This means you can listen for one of the spark events, and create a handler to e.g send out emails or otherwise notify the user of the action taken. I will not go into further detail on this note, as events are clearly explained in the Laravel documentation.
The Philosophy at large
Spark’s design is action-centric; it focuses on what happens within the application, and provides us programmers with an extendable set of tools to allow us to think in a more process- and action-oriented way. Most important, however, is that it wants us to focus less on what’s already there. As developers, it’s easy for us to often get lost in implementation details, and this is primarily what Spark is trying to avoid. Spark has all things implemented its own way, and we shouldn’t care too much about.
That being said, I personally am a very large fan of interactions, and the possible change in mindset this might lead to in terms of how we structure the development of an application. Perhaps, this is a way to easier think about the desired end result before the procedure of how it should be implemented, leading to better built, and more thought through applications.