Introducing Laravel Spark: Alpha Release
This is an alpha, experimental release of Spark. Things will change. Things will break. Thank you for testing
Yes, the much talked about Laravel Spark has finally been released. It’s the Alpha release with a better and solidified API. I’ll quickly give you an overview of Laravel Spark and how to use it for quick business SAAS application. I mean, that’s the main reason it was created.
Laravel Spark handles user authentications, user roles, plans and payments, billing system, payments coupons and team management.
Installation
Run this command to install the spark installer globally:
1 |
composer global require "laravel/spark-installer=~1.0" |
Once it’s installed. cd
to the directory you actually want your spark app to reside. Then run this command to create a new laravel app.
1 |
laravel new business-app |
Note: You must have the laravel installer globally to run this command below: If you don’t, run this command to install the laravel installer:
1 |
composer global require "laravel/installer=~1.1" |
Make sure to place the ~/.composer/vendor/bin
directory in your PATH so the laravel
executable can be located by your system.
cd
into the business-app, then run:
1 |
spark install |
There will be several prompts. Answer accordingly, but I chose yes for everything.
According to the Installation, you are required to go to your .env
file and assign values to the AUTHY_KEY, STRIPE_KEY and STRIPE_SECRET environment variables.
Homestead Users
During the installation process, it ran some migrations but for the homestead users, we’ll have to ssh into our box and do that ourselves.
Open up your Homestead.yaml
file, add a new database, set the path to your business-app
folder
Do the required assignment of ip address in your hosts file.
Run your migrations inside homestead.
By the time you are done, you would have something like this:
Invitations
, teams
and user_teams
tables have been added. In a default laravel installation, those 3 tables don’t exist.
So many files have been changed in this Spark Installation. Let’s look at one serious file that has been added.
The SparkServiceProvider.php file
SparkServiceProvider
As we know, provider files reside within the app/Providers directory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
<?php namespace AppProviders; use AppTeam; use Validator; use LaravelSparkSpark; use IlluminateHttpRequest; use LaravelSparkProvidersAppServiceProvider as ServiceProvider; class SparkServiceProvider extends ServiceProvider { /** * Meta-data included in invoices generated by Spark. * * @var array */ protected $invoiceWith = [ 'vendor' => 'Your Company', 'product' => 'Your Product', 'street' => 'PO Box 111', 'location' => 'Your Town, 12345', 'phone' => '555-555-5555', ]; /** * Bootstrap any application services. * * @return void */ public function boot() { parent::boot(); // } /** * Customize general Spark options. * * @return void */ protected function customizeSpark() { Spark::configure([ 'models' => [ 'teams' => Team::class, ] ]); } /** * Customize Spark's new user registration logic. * * @return void */ protected function customizeRegistration() { // Spark::validateRegistrationsWith(function (Request $request) { // return [ // 'name' => 'required|max:255', // 'email' => 'required|email|unique:users', // 'password' => 'required|confirmed|min:6', // 'terms' => 'required|accepted', // ]; // }); // Spark::createUsersWith(function (Request $request) { // // Return New User Instance... // }); } /** * Customize the roles that may be assigned to team members. * * @return void */ protected function customizeRoles() { Spark::defaultRole('member'); Spark::roles([ 'admin' => 'Administrator', 'member' => 'Member', ]); } /** * Customize the tabs on the settings screen. * * @return void */ protected function customizeSettingsTabs() { Spark::settingsTabs()->configure(function ($tabs) { return [ $tabs->profile(), $tabs->teams(), $tabs->security(), $tabs->subscription(), // $tabs->make('Name', 'view', 'fa-icon'), ]; }); Spark::teamSettingsTabs()->configure(function ($tabs) { return [ $tabs->owner(), $tabs->membership(), // $tabs->make('Name', 'view', 'fa-icon'), ]; }); } /** * Customize Spark's profile update logic. * * @return void */ protected function customizeProfileUpdates() { // Spark::validateProfileUpdatesWith(function (Request $request) { // return [ // 'name' => 'required|max:255', // 'email' => 'required|email|unique:users,email,'.$request->user()->id, // ]; // }); // Spark::updateProfilesWith(function (Request $request) { // // Update $request->user()... // }); } /** * Customize the subscription plans for the application. * * @return void */ protected function customizeSubscriptionPlans() { // Spark::free() // ->features([ // 'Feature 1', // 'Feature 2', // 'Feature 3', // ]); // Spark::plan('Basic', 'stripe-id')->price(10) // ->trialDays(7) // ->features([ // 'Feature 1', // 'Feature 2', // 'Feature 3', // ]); } } |
Let’s go through each major section:
$invoiceWith Array
It’s self-explanatory, just substitute your real details in the values of the array keys.
customizeSpark
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * Customize general Spark options. * * @return void */ protected function customizeSpark() { Spark::configure([ 'models' => [ 'teams' => Team::class, ] ]); } |
The method registers the team model. You can change it to whatever model you feel comfortable using.
customizeRegistration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/** * Customize Spark's new user registration logic. * * @return void */ protected function customizeRegistration() { // Spark::validateRegistrationsWith(function (Request $request) { // return [ // 'name' => 'required|max:255', // 'email' => 'required|email|unique:users', // 'password' => 'required|confirmed|min:6', // 'terms' => 'required|accepted', // ]; // }); // Spark::createUsersWith(function (Request $request) { // // Return New User Instance... // }); } |
This defines your registration logic . You can decide to have a custom Request file where you define all your validation rules and just inject it into the validateRegistrationsWith method and createUsersWith. That’ll work fine and make the method concise and clean
customizeRoles
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Customize the roles that may be assigned to team members. * * @return void */ protected function customizeRoles() { Spark::defaultRole('member'); Spark::roles([ 'admin' => 'Administrator', 'member' => 'Member', ]); } |
This method integrates with the new ACL feature added in Laravel 5.1.11. You can add other custom roles in the Spark roles
method.
customizeSettingsTabs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/** * Customize the tabs on the settings screen. * * @return void */ protected function customizeSettingsTabs() { Spark::settingsTabs()->configure(function ($tabs) { return [ $tabs->profile(), $tabs->teams(), $tabs->security(), $tabs->subscription(), // $tabs->make('Name', 'view', 'fa-icon'), ]; }); Spark::teamSettingsTabs()->configure(function ($tabs) { return [ $tabs->owner(), $tabs->membership(), // $tabs->make('Name', 'view', 'fa-icon'), ]; }); } |
This function allows you to add or remove links in the settings tab.
Add a new link by uncommenting the
1 |
$tabs->make('Name', 'view', 'fa-icon') |
It will create a new link called Name on the settings tab.
Same goes for the teamSettings tab.
customizeProfileUpdates
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * Customize Spark's profile update logic. * * @return void */ protected function customizeProfileUpdates() { // Spark::validateProfileUpdatesWith(function (Request $request) { // return [ // 'name' => 'required|max:255', // 'email' => 'required|email|unique:users,email,'.$request->user()->id, // ]; // }); // Spark::updateProfilesWith(function (Request $request) { // // Update $request->user()... // }); } |
This is similar to customizeRegistration. You can change the rules here to fit your style. I personally prefer creating a custom ProfileUpdate Request file where I define my validation rules and then inject them here into validateProfileUpdatesWith and updateProfilesWith methods
customizeSubscriptionPlans
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * Customize the subscription plans for the application. * * @return void */ protected function customizeSubscriptionPlans() { // Spark::free() // ->features([ // 'Feature 1', // 'Feature 2', // 'Feature 3', // ]); // Spark::plan('Basic', 'stripe-id')->price(10) // ->trialDays(7) // ->features([ // 'Feature 1', // 'Feature 2', // 'Feature 3', // ]); } |
This is where you adjust your subscription plans to meet your business needs. It’s so awesome that the boilerplate has been created for you easily. Let me show you a typical example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Spark::plan('Advanced', 'stripe-id')->price(50) ->trialDays(7) ->features([ 'Feature 1', 'Feature 2', ]); Spark::plan('Basic', 'stripe-id')->price(20) ->yearly() ->features([ 'Feature 1', ]); Spark::plan('Advanced', 'stripe-id')->price(50) ->yearly() ->features([ 'Feature 1', 'Feature 2', ]); |
Add this indicates on the UI that I have a Basic and Advanced plan. The Advanced plan has a free trial for 7 days. The Basic and Advanced plan is yearly with a subscription of 20 and 50 dollars respectively.
Just too awesome to have all that generated for you with just simple and minor adjustments to your code. Damn, too awesome!!!
Coupons
Laravel Spark offers coupons and discounts. Create a coupon in Stripe, then send the users coupon as a parameter when they visit the registration page like so
1 |
http://businessapp.com/register?coupon=couponcode |
Laravel Spark sees the coupon in the GET request, then automatically validates it from the Stripe API and applies the discount amount to the total like so:
You can also temporarily add a site-wide coupon by adding this in the boot() or customizeSpark() method in the SparkServiceProvider.php file.
1 |
Spark::promotion('coupon-code-here'); |
Terms and Conditions
Create a new file named terms.md
in the root directory and it will automatically be pulled in for the /terms
route.
Models
We have the User and Team Models that ships with the installation.
User.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<?php namespace App; use LaravelCashierBillable; use LaravelSparkTeamsCanJoinTeams; use IlluminateDatabaseEloquentModel; use IlluminateAuthPasswordsCanResetPassword; use IlluminateFoundationAuthAccessAuthorizable; use LaravelCashierContractsBillable as BillableContract; use IlluminateContractsAuthAccessAuthorizable as AuthorizableContract; use IlluminateContractsAuthCanResetPassword as CanResetPasswordContract; use LaravelSparkAuthTwoFactorAuthenticatable as TwoFactorAuthenticatable; use LaravelSparkContractsAuthTwoFactorAuthenticatable as TwoFactorAuthenticatableContract; class User extends Model implements AuthorizableContract, BillableContract, CanResetPasswordContract, TwoFactorAuthenticatableContract { use Authorizable, Billable, CanResetPassword, TwoFactorAuthenticatable; /** * The database table used by the model. * * @var string */ protected $table = 'users'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The accessors to append to the model's array form. * * @var array */ protected $appends = [ 'using_two_factor_auth' ]; /** * The attributes excluded from the model's JSON form. * * @var array */ protected $hidden = [ 'password', 'remember_token', 'two_factor_options', 'stripe_id', 'stripe_subscription', 'last_four', 'extra_billing_info' ]; /** * The attributes that should be mutated to dates. * * @var array */ protected $dates = [ 'trial_ends_at', 'subscription_ends_at', ]; } |
The Model implements TwoFactorAuthenticableContract. We have the $hidden array populated with more fields that needs to be hidden from a json response.
Team.php
1 2 3 4 5 6 7 8 9 10 |
<?php namespace App; use LaravelSparkTeamsTeam as SparkTeam; class Team extends SparkTeam { // } |
This is the boilerplate for the Team Model. You can add several methods to get the users that belong to a team, the owner of the team, invitations to a team and several other functionalities you might think of.
Laravel Spark also makes good use of Vue.js and JQuery. Check the resources/assets/js directory. Vue.js components are loaded for the views. That’s what allows for the slick dynamic switching in the UI.
I don’t know why Taylor is obsessed with Vue.js. I think it’s because Vue.js
is lightweight and very simple to use.
PS: This is just an introduction to the Alpha release of Laravel Spark. There might be other things that I didn’t cover, but I leave that as your assignment, dig it up to see the inner workings and let me know about your findings!
Please if you have any questions or observations, Feel free to drop it in the comments section below.
- How to build your own Youtube – Part 10 - August 1, 2016
- How to build your own Youtube – Part 9 - July 25, 2016
- How to build your own Youtube – Part 8 - July 23, 2016
- How to build your own Youtube – Part 6 - July 6, 2016
- Introducing Laravel Password v1.0 - July 3, 2016
- How to build your own Youtube – Part 5 - June 28, 2016
- How to build your own Youtube – Part 4 - June 23, 2016
- How to build your own Youtube – Part 3 - June 15, 2016
- How to build your own Youtube – Part 2 - June 8, 2016
- How to build your own Youtube – Part 1 - June 1, 2016