Model factory and custom validation artisan commands in Laravel 5.5

It’s always interesting when the next version of Laravel is around the corner. In addition to new features, there’s often significant improvements to existing ones.

Before a new version is officially released, one thing I like to do is clone the develop branch to see if there are any new artisan commands.

# If you have the Laravel Installer package installed
laravel new laravel-dev --dev
# Or use composer
composer create-project laravel/laravel=dev-develop laravel-dev
cd laravel-dev
php artisan

There are two new make commands — make:factory and make:rule


make:factory

In Laravel 5.4 or older, the convention is to add your model factories to the default database/factories/ModelFactory.php file . It’s perfectly fine for a few Models but the file will inevitably become more difficult to manage when you add more.

Laravel 5.4

You can still add new files manually in database/factories and Laravel will automatically load them.

// In Illuminate\Database\Eloquent\Factory::load()
/**
* Load factories from path.
*
* @param string $path
* @return $this
*/
public function load($path)
{
$factory = $this;

if (is_dir($path)) {
foreach (Finder::create()->files()->in($path) as $file) {
require $file->getRealPath();
}
}

return $factory;
}

However, this is probably not clear to most users who haven’t read the docs in detail. Also, it’s annoying to create new files manually and copy/paste the template.

In Laravel 5.5, you can use the new make:factory artisan command to generate individual model factory files along with the template.

php artisan make:factory Lead
php artisan make:factory Note
Laravel 5.5

Again, it’s not one of those big new features. But it’s a neat little addition to help you organise your code better.


make:rule

There is a new way to create custom validation rules in Laravel 5.5 and I find it much simpler. First, let’s review how you would perform a custom validation in a Laravel application currently.

Let’s say you have to validate a postcode field with some regex to ensure that it is a valid Australian postcode.

You first have to define the rules in a service provider:

// In \App\Providers\AppServiceProvider
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Validator::extend('valid_postcode', function ($attribute, $value) {
$regex = '/^((0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|(7[8-9][0-9]{2}))*$/';

return (bool) preg_match($regex, $value);
});
}

Then in a form request (or controller method), you can use valid_postcode as a rule:

// In \App\Http\Requests\SomeRequest
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'post_code' => 'required|valid_postcode',
];
}

public function messages()
{
return [
'valid_postcode' => 'Invalid postcode',
];
}

What if you also want to display the invalid value in the error message, e.g. Postcode 0000 is invalid? You can update the messages() method above to include a placeholder:

public function messages()
{
return [
'valid_postcode' => 'Postcode :value is invalid',
];
}

Then in AppServiceProvider you include another method Validator::replacer that takes the above message as a parameter and does a str_replace on it with the entered value.

// In \App\Providers\AppServiceProvider
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Validator::extend('valid_postcode', function ($attribute, $value) {
$regex = '/^((0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|(7[8-9][0-9]{2}))*$/';

return (bool) preg_match($regex, $value);
});

Validator::replacer('valid_postcode', function($message, $attribute) {
$value = Input::get($attribute);

return str_replace(':postcode', $value, $message);
});
}

Not bad. But here’s how you do the same thing above in Laravel 5.5:

Create your rule class with artisan:

php artisan make:rule ValidPostcode

This creates ValidPostcode.php in app/Rules directory. The class ValidPostcode has two methods passes and message.

class ValidPostcode implements Rule
{
const POSTCODE = '/^((0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|
(7[8-9][0-9]{2}))*$/';

private $value;

/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$this->value = $value;

return (bool) preg_match(self::POSTCODE, $value);
}

/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return "Postcode {$this->value} is invalid";
}
}

Then in your form request, you add an instance of your validation class to an array of rules for postcode.

// In \App\Http\Requests\SomeRequest
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'postcode' => [
'required',
new ValidPostcode,
]
];
}

This is a much cleaner way to create and maintain custom validations. You no longer need to define them in a service provider. It’s easier to manage string replacements in your error message. And if you use an IDE like PhpStorm, it’s also easier to navigate to your custom validation classes from the rules method.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.