Tuesday, August 11, 2015

Creating an advanced Invitation System in Laravel 4 Part 2

Creating an advanced Invitation System in Laravel 4 Part 2

Posted by on May 5th, 2014

Over the last two weeks I’ve been looking at building an invitation system in Laravel 4. First I looked at setting up a basic foundation that would allow a user to create an invite and then only allow valid invites to be able to register for the application.
Last week I looked at turning that simple foundation into a more robust process that included validation, a route filter and automatic invitation code generation.
In this week’s tutorial I’m going to be looking at building the following components:
  1. Allow existing users to invite new users
  2. Writing tests

Allow existing users to invite new users

An important aspect of social consumer applications is the ability to invite your friends to the application. Users are more likely to use an new application if people they know are already using it, and getting an invite from a friend is a much better first impression than landing on the site cold.

Recording available invitations

When a current user is logged in to the application, I want to be able to send them a notification when they have invitations to send to their friends.
The easiest way to do this is to add a column to the users table that will record how many invitations the current user has available:
$table->integer('invitations')->default(0);
Add this column to your migration by either creating a new migration or just chucking it in the users table migration if you haven’t shipped your application yet.
You will notice by default I’m setting the value to 0. In a future tutorial I will show you how the number of invitations and the rate of new users will be calculated.

The Inviter service

Last week I looked at creating a Requester service class that would be used to accept the invitation request, validate the input and then create and store the new invitation entity.
Inviting a new user is a similar process to requesting an invite, but it involves some slightly different rules and logic.
To encapsulate this process, I will be creating a new service class called Inviter.
The Inviter class will follow the same basic structure as the Requester class from last week. This means I can move some of the methods and duplicated code into an abstract class so they can both inherit it.
So the first thing to do is to create a new file called AbstractInviter.php:
<?php namespace Cribbb\Inviters;

use Exception;
use Cribbb\Validators\Validable;

abstract class AbstractInviter {

  /**
   * Run the validation checks on the input data
   *
   * @param array $data
   * @return bool
   */
  public function runValidationChecks(array $data)
  {
    foreach($this->validators as $validator)
    {
      if($validator instanceof Validable)
      {
        if(! $validator->with($data)->passes())
        {
          $this->errors = $validator->errors();
        }
      }

      else
      {
        throw new Exception("{$validator} is not an instance of Cribbb\Validiators\Validable");
      }
    }

    if($this->errors->isEmpty())
    {
      return true;
    }
  }

  /**
   * Return the errors message bag
   *
   * @return Illuminate\Support\MessageBag
   */
  public function errors()
  {
    return $this->errors;
  }

}
Next I can create the Inviter class:
<?php namespace Cribbb\Inviters;

use Illuminate\Support\MessageBag;
use Cribbb\Repositories\Invite\InviteRepository;

class Inviter extends AbstractInviter {

  /**
   * Invite Repository
   *
   * @var Cribbb\Repositories\Invite\InviteRepository
   */
  protected $inviteRepository;

  /**
   * An array of Validators
   *
   * @var array
   */
  protected $validators;

  /**
   * MessageBag errors
   *
   * @var Illuminate\Support\MessageBag;
   */
  protected $errors;

  /**
   * Create a new instance of the Invite Inviter
   *
   * @param Cribbb\Repositories\Invite\InviteRepository $inviteRepository
   * @param array $validators
   * @return void
   */
  public function __construct(InviteRepository $inviteRepository, array $validators)
  {
    $this->inviteRepository = $inviteRepository;
    $this->validators = $validators;

    $this->errors = new MessageBag;
  }

}
As you can see, so far this is pretty much exactly the same as the Requester class from last week.

Policies and business rules

The big difference between the Requester class and the Inviter class is, the Inviter class is subject to certain policies and business rules that must also be enforced.
When a user requests an invite, as long as their email address is valid we can add them straight into the list.
But the logic around an existing user inviting another user is slightly different and is an area of the code that would likely evolve due to the performance of the application.
For example, an important policy that we have to abide by is that the user must have assigned invitations. If the current user has made a request without an assigned invitation, we need to abort.
Imagine if you had hard coded that check into your create method, but then a couple of weeks down the line you needed to add another user check before allowing the invitation to be created. Do you create another if..else block to run the check? This situation will quickly get out of hand.
Instead I will create Policy classes that can be injected into the service in pretty much the same way as the Validator classes from last week. This means it will be easy to add additional policies into the service when things inevitably evolve in the future.
So the first thing to do is to create a Policies directory under Cribbb\Inviters.
Next I will create a Policy interface that all of my policies will implement:
<?php namespace Cribbb\Inviters\Policies;

use User;

interface Policy {

  /**
   * Run the policy check on the current user
   *
   * @param User $user
   * @return bool
   */
  public function run(User $user);

}
If one of the policies is not correctly satisfied, I want to be able to throw a custom exception so I can deal with the problem in a better way than a generic error message. I will create a custom exception to handle this situation:
<?php namespace Cribbb\Inviters\Policies;

use Exception;

class InvitePolicyException extends Exception {}
And finally I can create the individual policy classes:
<?php namespace Cribbb\Inviters\Policies;

use User;

class UserHasInvitations implements Policy {

  /**
   * Run the policy check on the current user
   *
   * @param User $user
   * @return bool
   */
  public function run(User $user)
  {
    if($user->invitations > 0)
    {
      return true;
    }

    throw new InvitePolicyException("{$user->name} does not have any invitations");
  }

}
As you can see, this tiny class has the sole responsibility of checking that the current user has assigned invitations.
Back in the Inviter class, I will modify the constructor to accept an array of policies:
/**
 * Create a new instance of the Invite Inviter
 *
 * @param Cribbb\Repositories\Invite\InviteRepository $inviteRepository
 * @param array $validators
 * @param array $policies
 * @return void
 */
public function __construct(InviteRepository $inviteRepository, array $validators, array $policies)
{
  $this->inviteRepository = $inviteRepository;
  $this->validators = $validators;
  $this->policies = $policies;

  $this->errors = new MessageBag;
}
And I will add the create method so that it runs through each of the policies before creating the new invitation:
/**
 * Create a new Invite
 *
 * @param array User
 * @param array $data
 * @return Illuminate\Database\Eloquent\Model
 */
public function create(User $user, $data)
{
  foreach($this->policies as $policy)
  {
    if($policy instanceof Policy)
    {
      $policy->run($user);
    }

    else
    {
      throw new Exception("{$policy} is not an instance of Cribbb\Inviters\Policies\Policy");
    }
  }

  if($this->runValidationChecks($data))
  {
    return $this->inviteRepository->create($data);
  }
}
Finally in the InvitersServiceProvider I can set up the service to resolve correctly out of the IoC container with the correct dependencies injected:
/**
 * Register the Inviter service
 *
 * @return void
 */
public function registerInviter()
{
  $this->app->bind('Cribbb\Inviters\Inviter', function($app){
    return new Inviter(
      $this->app->make('Cribbb\Repositories\Invite\InviteRepository'),
      array( new EmailValidator($app['validator']) ),
      array( new UserHasInvitations )
    );
  });
}

Writing tests

Something that I’ve neglected so far is writing tests. I won’t go into detail about testing every single character of the Invitation component, instead I’ll just show you a couple of tests that assert that the component works as it is intended to.
Firs set up your basic test file structure:
<?php

class InvitersTest extends TestCase {

  public function setUp()
  {
    parent::setUp();
  }

  public function tearDown()
  {

  }

}
The first test I will write will be test the request a new invite process:
public function testRequestNewInvitation()
{
  $requester = App::make('Cribbb\Inviters\Requester');

  $invite = $requester->create(array('email' => 'name@domain.com'));
  $this->assertInstanceOf('Invite', $invite);

  $invite = $requester->create(array('email' => ''));
  $this->assertTrue(is_null($invite));
  $this->assertEquals(1, count($requester->errors()));
}
In this test first I request an invite with a valid email address and assert that I’m returned an instance of the model.
Next I request an invite with an invalid email. This time I assert that the returned value is null and the number of errors is 1.
Whilst writing these tests, I encountered a weird Eloquent issue where the model events do not seem to flush themselves between tests. This seems to be a known issue.
A workaround is to add the following method to your test class and call it in the setUp() method like this:
public function setUp()
{
  parent::setUp();
  $this->resetEvents();
}

private function resetEvents()
{
  $models = array('Invite');

  foreach ($models as $model)
  {
    call_user_func(array($model, 'flushEventListeners'));
    call_user_func(array($model, 'boot'));
  }
}
Next I will test inviting another user to the application:
public function testUserInviteAnotherUser()
{
  $inviter = App::make('Cribbb\Inviters\Inviter');
  $user = new User;
  $user->invitations = 1;

  $invite = $inviter->create($user, array('email' => 'name@domain.com'));
  $this->assertInstanceOf('Invite', $invite);
}
Again, here I’m ensuring that when a user with an assigned invitation invites another user with a valid email address, the returned value is an instance of Invite.
And finally, when a user without any assigned invites tries to invite another user, we should get an exception:
/**
 * @expectedException Cribbb\Inviters\Policies\InvitePolicyException
 */
public function testUserHasNoInvitations()
{
  $inviter = App::make('Cribbb\Inviters\Inviter');
  $user = new User;
  $invite = $inviter->create($user, array('email' => 'name@domain.com'));
}

Conclusion

In this tutorial I’ve looked at building a service class to allow current users to invite their friends to the application.
I think the most important part of this tutorial was looking at introducing policies to our code. This means when the rules around the component inevitably change, we don’t have to start messing with the class. Instead we can just inject another policy.
At first it can seem a bit redundant to write a class with a single method. However these tiny little classes are much nicer to work with as they are easier to understand, test, and combine in different ways to get your desired outcome. Big monolithic classes quickly become a headache, but tiny little classes continue to be a breeze to work with.
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.
To view a full listing of the tutorials in this series, click here.

Creating an advanced Invitation System in Laravel 4 Part 1

Creating an advanced Invitation System in Laravel 4 Part 1

Posted by on April 28th, 2014

Last week I looked at setting up a basic invitation system in Laravel 4. I looked at creating the invites table, the model and the repository and I showed you how to very easily create a new invitation and check for valid invitations when a new user tries to sign up for your application.
This basic foundation has put the right components together, but there is still quite a bit missing from the system to make it robust and capable of meeting all of my requirements.
In this week’s tutorial I’m going to look at building on top of last week’s foundation. In today’s tutorial I’ll be looking at:
  1. Automatically generating the invitation code
  2. Validation through a service class
  3. A filter to protect the registration route

Automatically generating the invitation code

The first thing I will do is to clean up how the invitation code is generated. You might remember from last time that I was creating the code in the create() method of the EloquentInviteRepository. This did the job but it wasn’t ideal.
The invitation code should be generated only when the new invitation is created. This means it will never have to be updated so we don’t need to pollute any of the other code with references to it. Instead, the code should just be generated for us.
Fortunately, Eloquent fires events that allow you to hook on to the various stages of the model’s lifecycle. This means we can write code that should be run based upon what the model is doing.
You can read more about the Eloquent’s Model Events and what you have available to you in the documentation.
In this situation, I want to hook on to the creating event to automatically generate the invitation code before the model is saved.
The first thing to do is to remove the code from the fillable array in the Invite model. This will prevent the code from ever being mass-assigned:
/**
 * Properties that can be mass assigned
 *
 * @var array
 */
protected $fillable = array('email');
Next I need to override the static boot method to register my event:
/**
 * Register the model events
 *
 * @return void
 */
protected static function boot()
{
  parent::boot();

  static::creating(function($model)
  {
    $model->generateInvitationCode();
  });
}
First you need to call the boot method on the parent Model class.
Next you can call the event method and pass in a closure. In this example, I’m calling the creating method.
The closure accepts an instance of the $model. I will then call the generateInvitationCode() method so I don’t end up with lots of code in this closure.
Finally, my generateInvitationCode() looks like this:
/**
 * Generate an invitation code
 *
 * @return void
 */
protected function generateInvitationCode()
{
  $this->code = bin2hex(openssl_random_pseudo_bytes(16));
}
As you can see, I simply set the randomly generated code to the code property. There’s no need to call the save() method as this will automatically called as part of the model’s lifecycle.
Now I can clean up the create() method on the EloquentInviteRepository as the code will be automatically generated:
/**
 * Create
 *
 * @param array $data
 * @return Illuminate\Database\Eloquent\Model
 */
public function create(array $data)
{
  return $this->model->create($data);
}

Validation through a service class

One of the problems of the implementation from last week was, there was no validation to prevent someone from requesting multiple invites for the same email address.
In order to ensure that Cribbb will only accept valid data I need to insert a layer of validation before the invitation is created.
If you remember back to Advanced Validation as a Service for Laravel 4, I’ve already got a foundation for creating validation classes in place.
However, should this go in the Controller? Or in the Repository? Or somewhere else?
In this instance I’m going to create a new service class for creating invitations. The reason for this is, I’m going to need to have different ways of creating invitations, and so separating the logic into it’s own class makes more sense than trying to crowbar it into the repository or duplicating my efforts in the controller.
The invitation classes are their own component of Cribbb and so they deserve their own namespace. I’m going to keep all of these classes under the Cribbb\Inviters namespace.
The class I will create is for requesting a new invite. This class will be called Requester.
Here is the basic outline for Requester.php:
<?php namespace Cribbb\Inviters;

use Illuminate\Support\MessageBag;
use Cribbb\Repositories\Invite\InviteRepository;

class Requester {

  /**
   * Invite Repository
   *
   * @var Cribbb\Repositories\Invite\InviteRepository
   */
  protected $inviteRepository;

  /**
   * MessageBag errors
   *
   * @var Illuminate\Support\MessageBag;
   */
  protected $errors;

  /**
   * Create a new instance of the Invite Requester
   *
   * @param Cribbb\Repositories\Invite\InviteRepository $inviteRepository
   * @return void
   */
  public function __construct(InviteRepository $inviteRepository)
  {
    $this->inviteRepository = $inviteRepository;

    $this->errors = new MessageBag;
  }

  /**
   * Return the errors message bag
   *
   * @return Illuminate\Support\MessageBag
   */
  public function errors()
  {
    return $this->errors;
  }

}
As you can see from the code above, I’m injecting the InviteRepository into this class and setting the instance as a class property. This means I no longer need to inject the repository into the controller.
I will also set up the $errors property as a new instance of MessageBag. I prefer this to be an empty instance of MessageBag rather than setting it as null because the class has a couple of nice public methods for working with an empty bag. You will also notice that I instantiate a new instance of MessageBag inside the controller, rather than injecting it. This violates the dependency injection principle, but I think of the MessageBag as an enhancement of the class, rather than a dependency, so I’m cool with that.
Next I need to create a validator class that will validate the request email address. Under Cribbb\Inviters I will create a new directory called Validators to hold my validator classes.
To validate email addresses, I will use the following class:
<?php namespace Cribbb\Inviters\Validators;

use Cribbb\Validators\Validable;
use Cribbb\Validators\LaravelValidator;

class EmailValidator extends LaravelValidator implements Validable {

  /**
   * Validation rules
   *
   * @var array
   */
  protected $rules = array(
    'email' => 'required|email|unique:users,email|unique:invites,email'
  );

}
If the above code doesn’t make sense, read my post on Advanced Validation as a Service for Laravel 4.
Now that I’ve got my validator class set up, I need to inject it into the Request class. Due to the evolving nature of this kind of code, I will inject my Validable class as an array into the class. This means should I want to add more Validable instances, I won’t have to modify the Request class.
My __construct() method now looks like this:
/**
 * An array of Validators
 *
 * @var array
 */
protected $validators;

/**
 * Create a new instance of the Invite Requester
 *
 * @param Cribbb\Repositories\Invite\InviteRepository $inviteRepository
 * @param array $validators
 * @return void
 */
public function __construct(InviteRepository $inviteRepository, array $validators)
{
  $this->inviteRepository = $inviteRepository;
  $this->validators = $validators;

  $this->errors = new MessageBag;
}
This might seem like an over-engineered solution, and for a lot of instances it probably is. If your class will never need to be modified for evolving business rules, then injecting the single class would be totally fine too.
Finally I can write the create() method that will actually create the new invitation:
/**
 * Create a new Invite
 *
 * @param array $data
 * @return Illuminate\Database\Eloquent\Model
 */
public function create(array $data)
{
  foreach($this->validators as $validator)
  {
    if($validator instanceof Validable)
    {
      if(! $validator->with($data)->passes())
      {
        $this->errors = $validator->errors();
      }
    }

    else
    {
      throw new Exception("{$validator} is not an instance of Cribbb\Validiators\Validable");
    }
  }

  if($this->errors->isEmpty())
  {
    return $this->inviteRepository->create($data);
  }
}
First I spin through all of the validators and check that they are an instance of Cribbb\Validators\Validable. If any one of the array values is not an instance of Validable I will throw an Exception. You could argue that the Exception is a bit overkill, but I think for instances where we are accepting data into the application, it’s better to be safe than sorry.
Next I check that the $data meets the requirements of the rules of the validator. If it does not, I will add the errors to the $errors class property.
Finally if there are no errors I will pass the data to the repository to create the new invitation.
The full Request class looks like this:
<?php namespace Cribbb\Inviters;

use Exception;
use Cribbb\Validators\Validable;
use Illuminate\Support\MessageBag;
use Cribbb\Repositories\Invite\InviteRepository;

class Requester {

  /**
   * Invite Repository
   *
   * @var Cribbb\Repositories\Invite\InviteRepository
   */
  protected $inviteRepository;

  /**
   * An array of Validators
   *
   * @var array
   */
  protected $validators;

  /**
   * MessageBag errors
   *
   * @var Illuminate\Support\MessageBag;
   */
  protected $errors;

  /**
   * Create a new instance of the Invite Creator
   *
   * @param Cribbb\Repositories\Invite\InviteRepository $inviteRepository
   * @param array $validators
   * @return void
   */
  public function __construct(InviteRepository $inviteRepository, array $validators)
  {
    $this->inviteRepository = $inviteRepository;
    $this->validators = $validators;

    $this->errors = new MessageBag;
  }

  /**
   * Create a new Invite
   *
   * @param array $data
   * @return Illuminate\Database\Eloquent\Model
   */
  public function create(array $data)
  {
    foreach($this->validators as $validator)
    {
      if($validator instanceof Validable)
      {
        if(! $validator->with($data)->passes())
        {
          $this->errors = $validator->errors();
        }
      }

      else
      {
        throw new Exception("{$validator} is not an instance of Cribbb\Validiators\Validable");
      }
    }

    if($this->errors->isEmpty())
    {
      return $this->inviteRepository->create($data);
    }
  }

  /**
   * Return the errors message bag
   *
   * @return Illuminate\Support\MessageBag
   */
  public function errors()
  {
    return $this->errors;
  }

}

Injecting the service class into the Controller

Now that I’ve set up the Request service class I can inject it into the Controller to replace the injected repository:
<?php

use Cribbb\Inviters\Requester;

class InviteController extends BaseController {

  /**
   * The Invite Request service
   *
   * @var Cribbb\Inviters\Requester
   */
  protected $requester;

  /**
   * Create a new instance of the InviteController
   *
   * @param Cribbb\Inviters\Requester
   */
  public function __construct(Requester $requester)
  {
    $this->requester = $requester;
  }

  /**
   * Create a new invite
   *
   * @return Response
   */
  public function store()
  {
    $invite = $this->requester->create(Input::all());

    if($invite)
    {
      // yay
    }

    // oh no
    $this->requester->errors();
  }

}
As you can see from the code above, this is a simple switch-a-roo of the repository for the service class. However, now we don’t have any weird logic in the controller, and we can report errors back if the input data did not meet the requirements.

A filter to protect the registration route

Another problem the initial implementation had was protecting certain routes to only allow user’s with a valid invitation. This is required so I can limit who has access to the registration form.
In last week’s tutorial I simply checked for a valid invitation in the Controller method. This works, but it’s a kinda messy way of doing it. The valid invite check isn’t really the concern of the Controller method, and if I needed that same logic in another method on a different Controller I would have to duplicate my code.
Instead I can create a filter to ensure only users with a valid invitation are allowed to hit the route. If the user does not have a valid invitation I can just 404 the response because I can just pretend the registration route does not exist.
If you are new to Laravel filters, I would recommend that you also read How to use Laravel 4 Filters.
Here is my route filter:
/*
|--------------------------------------------------------------------------
| Invitation Filter
|--------------------------------------------------------------------------
|
| The invite filter will only allow requests that include a valid
| "code" as part of the query string to access the given route.
| If the request does not have a valid code in the query
| string a "404 Not Found" response will be returned
|
*/

Route::filter('invite', function()
{
  if (! Input::has('code'))
  {
    App::abort(404);
  }

  $repository = App::make('Cribbb\Repositories\Invite\InviteRepository');

  if(! $repository->getValidInviteByCode(Input::get('code')))
  {
    App::abort(404);
  }
});
First I check to see if the request has a code parameter. If the request does not have a code, we can just bail out here.
Next I resolve an instance of the InviteRepository out of the IoC container. I then check to see if the code in the request is valid. If the code is not valid I can throw a 404 error.
Remember, a request is considered valid if the filter does not throw an exception or return a response.
Next in my RegisterContoller I can delete the checking logic from last week and replace it with the beforeFilter:
<?php

class RegisterController extends BaseController {

  /**
   * Create a new instance of the RegisterController
   *
   * @return void
   */
  public function __construct()
  {
    $this->beforeFilter('invite', array('only' => 'index'));
  }

  /**
   * Display the form for creating a new user
   *
   * @return View
   */
  public function index()
  {
    return 'Sign up here';
  }

}
Now if you try to hit the /register route, you should be returned a 404 Not Found exception. Next, sign up with a valid email and grab the code from the database. Now if you try to hit /register?code=your_code you should be allowed to hit the index method on the controller.

Conclusion

In part 1 of building an advanced invitation system, I looked at leveraging Eloquent’s event lifecycle to automatically generate invitation codes, and setting up a service class to deal with the logic of validating and creating a new invitation.
I think the thing to take away from this tutorial is, how much easier your life will be if you fence off bits of functionality so you don’t have to repeat yourself and you don’t have to rethink about how something should be implemented.
Generating the invitation codes should only happen when the invite is first created and should just be taken care for us just like the auto-incrementing id. None of the code for working with an invitation should need to know how to generate a code.
The question of should I put this in the Controller, the Repository or should I create a new Service class comes up often. My response is usually, “well, it depends”. In this example, I think you definitely do need a separate service class. In my case, there will be multiple ways for creating invitations in my application and the rules around what is considered “valid” data required for a new invitation is also likely to change. Hopefully this was a solid example of how to make the decision that you can use in your applications.
Over the next couple of weeks I will look at how existing users can invite new users, how to create a queue jump system and how to build a family tree that will record how new users were invited to the application.
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.
To view a full listing of the tutorials in this series, click here.

Creating an advanced Invitation System in Laravel 4 Part 3

Creating an advanced Invitation System in Laravel 4 Part 3

Posted by on May 12th, 2014

The build up towards launching a new consumer web application is almost as important as the actual application itself. Launching to the world without any kind of groundswell will more often than not be a failure.
Building an invitation process pre-launch is certainly not a new idea. Many different types of applications have launched with a clever invite system to both success and failure.
In my opinion, one of the best features of a good invite system is the incentive for people who request an invite to share their link with their followers on Twitter and Facebook. By sharing their link they will get into the application quicker, and it’s always good to be the person who knows the new hot thing.
In this week’s tutorial I’m going to show you how to build this into the functionality of the Cribbb invitation system. If you haven’t already read the first two tutorials, go read them now!
  1. Creating an advanced Invitation System in Laravel 4 Part 1
  2. Creating an advanced Invitation System in Laravel 4 Part 2

How this is going to work

So just to be clear, here is how I envision the Cribbb invitation system will work.
When a new user requests an invite, they submit their email address to get on the invitation list.
This will present them with a url that they can share on Twitter and Facebook to spread the word and jump the queue.
When someone clicks on the link, the referral code will be recorded in the session. If the person requests an invite too, the original invite will increase in rank.
Once I’m ready to start accepting new users, I will sort the invites table by the number of referrals to find the next batch of users to invite.

Updating the database tables

The first thing I will do is to update the invites table with some additional columns:
$table->string('invitation_code');
$table->string('referral_code');
$table->integer('referral_count')->default(0);
Here I’ve added a referral_code string field and a referral_count integer field that defaults to 0. You will also notice that I’ve renamed the code field to invitation_code to differentiate between the two codes. This meant I also had to update the getValidInviteByCode() in the EloquentInviteRepository class.

Updating the model

Now that I need to generate two codes for the model when it is created instead of one, I can tweak the existing boot() and generateCode() methods:
/**
 * Register the model events
 *
 * @return void
 */
protected static function boot()
{
  parent::boot();

  static::creating(function($model)
  {
    $model->invitation_code = $model->generateCode();
    $model->referral_code   = $model->generateCode();
  });
}

/**
 * Generate a code
 *
 * @return string
 */
protected function generateCode()
{
  return bin2hex(openssl_random_pseudo_bytes(16));
}
This should be pretty self explanatory as I’m simply calling the generateCode() method twice and assigning the returned value to the model’s class properties before it is saved to the database.

Storing the referral code

When a user requests an invite, they will be presented with a url in the form of http://cribbb.com?referral=referral_code. To keep things simple, I’m only going to store the referral_code in the session from the HomeController:
/**
 * Index
 *
 * @return View
 */
public function index()
{
  $this->storeReferralCode();

  return View::make('home.index');
}

/**
 * Store Referral code
 *
 * @return void
 */
protected function storeReferralCode()
{
  if(Input::has('referral'))
  {
    Session::put('referral_code', Input::get('referral'));
  }
}
If you wanted to check for a referral code in the query string and store it in the session for every page of your application, or even just a handful of pages you would be better of moving this to either the App::before filter or into the __construct() method of the Controller class.

Use referral_code when requesting a new invitation

Now that the referral_code will be stored in the session when a new user is referred, I need to update the Request class for creating a new invitation.
Firstly, in the InviteController update the store() method to submit the referral_code from the session if it is available:
/**
 * Create a new invite
 *
 * @return Response
 */
public function store()
{
  $invite = $this->requester->create(Input::all(), Session::get('referral_code', null));
}
The null value as the second parameter to the get() method is a default value if the value you are looking for is not available.
Next I can update the create() method on the Requester class:
/**
 * Create a new Invite
 *
 * @param array $data
 * @return Illuminate\Database\Eloquent\Model
 */
public function create(array $data, $referral = null)
{
  if($this->runValidationChecks($data))
  {
    if($referral)
    {
      $referer = $this->inviteRepository->getBy('referral_code', $referral)->first();

      if($referer)
      {
        $referer->increment('referral_count');
      }
    }

    return $this->inviteRepository->create($data);
  }
}
Firstly I’ve set the $referral as null by default so this method does not have to be hampered by requiring a $referral.
If the $data passes the validation checks the method will check to see if a $referral has been passed. Next It will attempt to find the $referer from the $inviteRepository by the referral_code.
If a $referer is found, the referrals counter is incremented.
Finally the invitation is created in the InviteRepository and returned.

Testing

To ensure that the process works correctly, I can use the following test:
public function testRequestSentByReferral()
{
  $requester = App::make('Cribbb\Inviters\Requester');
  $invite1 = $requester->create(array('email' => 'name@domain.com.com'));

  $invite2 = $requester->create(array('email' => 'other@domain.com'), $invite1->referral_code);

  $invite1 = Invite::find($invite1->id);
  $this->assertEquals(1, count($invite1->referral_count));
}
In this test I create an invitation and then use the referral_code to create a second invitation.
The test here is to ensure that the invitation’s referral_count is incremented when it’s referal_code is used to refer another user.

The invitation family tree

The final aspect of the invitation system that I wanted to build was to be able to generate a “family tree” of how user’s were invited by other users. I really like the way in Dribbble you can see which users were invited by another user.
A user will be invited by one user, and they might subsequently invited many other users. This is a classic one to many relationship, but we are linking two entities from the same table.

Add the invited_by

So the first thing to do is to add a column to the users to store the invited_by id.
$table->integer('invited_by')->nullable();
This column will store the id of the user who invited the current user. A user will only have a invited_by if they were invited by another user and so I will make this field nullable.
Again as I mentioned last week, if you haven’t shipped your application yet, just throw this line into the current users table migration.

Add the referrer_id

In order to know who invited the new user when they are registering for an account, I need to be able to track who give them the invitation. Add the following line to your invites table migration:
$table->integer('referrer_id')->nullable();
Next I need to set the referrer_id when an existing users invites another user.
In Inviter.php update the create() method to merge in the referrer_id form the User entity:
/**
 * Create a new Invite
 *
 * @param array User
 * @param array $data
 * @return Illuminate\Database\Eloquent\Model
 */
public function create(User $user, $data)
{
  $data = array_merge($data, ['referrer_id' => $user->id]);

  foreach($this->policies as $policy)
  {
    if($policy instanceof Policy)
    {
      $policy->run($user);
    }

    else
    {
      throw new Exception("{$policy} is not an instance of Cribbb\Inviters\Policies\Policy");
    }
  }

  if($this->runValidationChecks($data))
  {
    return $this->inviteRepository->create($data);
  }
}
Finally I can update the test to ensure this functionality is working correctly:
public function testUserInviteAnotherUser()
{
  $inviter = App::make('Cribbb\Inviters\Inviter');
  $user = User::create(['username' => 'philipbrown', 'email' => 'name@domain.com']);
  $user->invitations = 1;
  $invite = $inviter->create($user, ['email' => 'other@domain.com']);
  $this->assertInstanceOf('Invite', $invite);
  $this->assertEquals(1, $invite->referrer_id);
}
In this test I’m asserting that the referrer_id has been set correctly.
Now when a new user uses an invitation to register with Cribbb I can take the referrer_id and set the invited_by column on the new user entity.
Within Cribbb I probably use these family tree like structure to display how users know each other on a user’s profile page, although there are many other possible things you could do with this kind of data.

Conclusion

This is the final tutorial in the mini series of building an invitation system. Hopefully over the last couple of weeks you will have seen my iterative process for creating a process such as this.
I think it’s very important to take these kinds of things one step at a time. In the first tutorial, my code was very basic, but it did do the job it was intending to do.
Over the last three weeks I’ve taken each part of the process and either updated it or introduced my desired functionality. I think when you try to bite off more than you can chew, you end up in the weeds of implementation details and you lose sight of what you are actually trying to accomplish.
By breaking it down into much smaller, achievable chunks we can make progress and end up with easier to understand and maintain code.
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.
To view a full listing of the tutorials in this series, click here.

Wednesday, May 27, 2015

Contoh penggunaan facade route::

1. GET Route - terima URI:

Route::get('/', function()
{
    return 'Hello World';
});

2. Routes POST, PUT, DELTE :

Route::post('foo/bar', function()
{
    return 'Hello World';
});

Route::put('foo/bar', function()
{
    //
});

Route::delete('foo/bar', function()
{
    //
});

3. Route untuk verb berlainan :

Route::match(['get', 'post'], '/', function()
{
    return 'Hello World';
});

4. Route semua HTTP Verb :

Route::any('foo', function()
{
    return 'Hello World';
});



nota : 

- url helper:

$url = url('foo');

- masukkan token :
 
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
 
atau untuk blade :
 
<input type="hidden" name="_token" value="{{ csrf_token() }}">

 5. addtional method

<form action="/foo/bar" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
</form>

6. Route Parameter
 
Route::get('user/{id}', function($id)
{
    return 'User '.$id;
});


- dengan pilihan

Route::get('user/{name?}', function($name = null)  
{ 
   return $name; 
});
- dengan pilihan dan default

Route::get('user/{name?}', function($name = 'John')  
{ 
   return $name; 
});

7. Menggunakan Regular Expression :

Route::get('user/{name}', function($name)
{
    //
})
->where('name', '[A-Za-z]+');

Route::get('user/{id}', function($id)
{
    //
})
->where('id', '[0-9]+');

 

8. Kekangan array :

Route::get('user/{id}/{name}', function($id, $name)
{
    //
})
->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

 

8. Definisi Global Patterns

set dalam RouteServiceProvider:
 
$router->pattern('id', '[0-9]+');
 
 
Route::get('user/{id}', function($id)
{
    // Only called if {id} is numeric.
});

 

9. semak parameter Route :

 
if ($route->input('id') == 1)
{
    //
}


atau

use Illuminate\Http\Request;

Route::get('user/{id}', function(Request $request, $id)  
{ 
   if ($request->route('id'))  
   { 
      // 
   } 
});


11. Namakan routes

Route::get('user/profile', ['as' => 'profile', function()
{
    //
}]);
 
 
You may also specify route names for controller actions:
 
Route::get('user/profile', [
    'as' => 'profile', 'uses' => 'UserController@showProfile'
]);

Now, you may use the route's name when generating URLs or redirects:
 
$url = route('profile');

$redirect = redirect()->route('profile');


The currentRouteName method returns the name of the route handling the current request:
$name = Route::currentRouteName();

Route Groups

Sometimes many of your routes will share common requirements such as URL segments, middleware, namespaces, etc. Instead of specifying each of these options on every route individually, you may use a route group to apply attributes to many routes.
Shared attributes are specified in an array format as the first parameter to the Route::group method.

Middleware

Middleware are applied to all routes within the group by defining the list of middleware with the middleware parameter on the group attribute array. Middleware will be executed in the order you define this array:
Route::group(['middleware' => ['foo', 'bar']], function()
{
    Route::get('/', function()
    {
        // Has Foo And Bar Middleware
    });

    Route::get('user/profile', function()
    {
        // Has Foo And Bar Middleware
    });

});

Namespaces

You may use the namespace parameter in your group attribute array to specify the namespace for all controllers within the group:
Route::group(['namespace' => 'Admin'], function()
{
    // Controllers Within The "App\Http\Controllers\Admin" Namespace

    Route::group(['namespace' => 'User'], function()
    {
        // Controllers Within The "App\Http\Controllers\Admin\User" Namespace
    });
});
Note: By default, the RouteServiceProvider includes your routes.php file within a namespace group, allowing you to register controller routes without specifying the full App\Http\Controllers namespace prefix.

Sub-Domain Routing

Laravel routes also handle wildcard sub-domains, and will pass your wildcard parameters from the domain:

Registering Sub-Domain Routes

Route::group(['domain' => '{account}.myapp.com'], function()
{

    Route::get('user/{id}', function($account, $id)
    {
        //
    });

});

Route Prefixing

A group of routes may be prefixed by using the prefix option in the attributes array of a group:
Route::group(['prefix' => 'admin'], function()
{
    Route::get('users', function()
    {
        // Matches The "/admin/users" URL
    });
});
You can also utilize the prefix parameter to pass common parameters to your routes:

Registering a URL parameter in a route prefix

Route::group(['prefix' => 'accounts/{account_id}'], function()
{
    Route::get('detail', function($account_id)
    {
        //
    });
});
You can even define parameter constraints for the named parameters in your prefix:
Route::group([
    'prefix' => 'accounts/{account_id}',
    'where' => ['account_id' => '[0-9]+'],
], function() {

    // Define Routes Here
});

Route Model Binding

Laravel model binding provides a convenient way to inject class instances into your routes. For example, instead of injecting a user's ID, you can inject the entire User class instance that matches the given ID.
First, use the router's model method to specify the class for a given parameter. You should define your model bindings in the RouteServiceProvider::boot method:

Binding A Parameter To A Model

public function boot(Router $router)
{
    parent::boot($router);

    $router->model('user', 'App\User');
}
Next, define a route that contains a {user} parameter:
Route::get('profile/{user}', function(App\User $user)
{
    //
});
Since we have bound the {user} parameter to the App\User model, a User instance will be injected into the route. So, for example, a request to profile/1 will inject the User instance which has an ID of 1.
Note: If a matching model instance is not found in the database, a 404 error will be thrown.
If you wish to specify your own "not found" behavior, pass a Closure as the third argument to the model method:
Route::model('user', 'User', function()
{
    throw new NotFoundHttpException;
});
If you wish to use your own resolution logic, you should use the Route::bind method. The Closure you pass to the bind method will receive the value of the URI segment, and should return an instance of the class you want to be injected into the route:
Route::bind('user', function($value)
{
    return User::where('name', $value)->first();
});

Throwing 404 Errors

There are two ways to manually trigger a 404 error from a route. First, you may use the abort helper:

abort(404);



 
 




 

Tuesday, May 26, 2015

Contoh penggunaan facade session::

1. Simpan item dalam session :

Session::put('key', 'value'); 
 
method :
 
 session(['key' => 'value']);

2. masukkan nilan dalam item session :

Session::push('user.teams', 'developers');

3. Dapatkan item dalam session :

$value = Session::get('key'); 

method :
 
$value = session('key');

4. Dapatkan item dari session :

$value = Session::get('key', 'default'); 

atau 
 
$value = Session::get('key', function() { return 'default'; });

5. Dapatkan Item dan lupakan :

$value = Session::pull('key', 'default');

6. Datapakan semua data dari session :

$data = Session::all();

7. Pastikan item wujud dalam session :

if (Session::has('users')) { // }

8. Buang item dalam session : 

Session::forget('key');

9. Buang semua item dalam session :

Session::flush();

10. Bina id session baru :

Session::regenerate();

 


flash data : simpan data untuk request yang seterusnya.


11. Reflash data semasa untuk request seterusnya :

Session::reflash();

12. Reflash habya sebahagian Flash Data

Session::keep(['username', 'email']);



Guna database sebagai session data :

Contoh  Schema  table:
 
Schema::create('sessions', function($table)
{
    $table->string('id')->unique();
    $table->text('payload');
    $table->integer('last_activity');
});

atau
 
php artisan session:table

composer dump-autoload

php artisan migrate