frameworks Archives - developed.be

  • Published:September 18th, 2015
  • Category:Lavarel

Use the underneath snippet if you want to match an entire word in a route pattern for Laravel.

Route::pattern('my_word_pattern', '^myword$');
Route::get('{my_word_pattern}/...

I spent a crazy amount of time on figuring out how to validate form arrays in Laravel. There is some official documentation, but like most official documentation of Laravel, it only covers the bare minimum of what you need to know. This is an advanced article on how to validate form arrays in Laravel.

I have a form where people can enter 3 iban and bic numbers (these are EU bankaccounts). That makes 3 pairs of textboxes:

iban[]

bic[]

iban[]

bic[]

iban[]

bic[]

… and other form elements …

My desired form validation rules:

  • Maximum 3 couples of iban & bic can be submitted
  • The user is not obligated to fill in any of the iban & bic numbers.
  • When an IBAN is filled in, the user also needs to fill in the BIC.
  • The IBAN and BIC can only contain alphanumerics and spaces.

Out of the box, Laravel can validate form arrays with the dot character. The next form rule will work out of the box:

'iban.0' => 'required'

in your views, you can check for the error:

$errors->has('iban.0')

The same goes for the second iban:

'iban.1' => 'required'

But I don’t need that in my setup. The fields are not mandatory.

To check for alpha+num+spaces you have to create a new ValidationRule. The most decent way is to extend the default Validator class and add your own rules. Then you have to create a service provider that returns the ExtendedProvider. Finally you have to add the serviceprovider to app.php and run composer update.

I’ll walk you through each of the files you have to create:

File: ExtendedValidator.php

<?php namespace App\Services\Validators;
 
use Illuminate\Validation\Validator;
 
class ExtendedValidator extends Validator {
 
    public function validateAlphaNumSpaces($attribute, $value, $parameters)
    {
        return preg_match('/^[\pL\s0-9]+$/u', $value);
    }
}

The function validateAlphaNumSpaces() will listen to the rule alpha_num_spaces. The three parameters are standard parameters for validate-functions. We only use $value and not the other parameters because this is a simple rule. $value is what the user entered in the form field.

The function checks $value with a regex and returns true if it matches.

File: ExtendedServiceProvider.php

<?php namespace App\Services\Validators;
 
use Illuminate\Support\ServiceProvider;
 
class ExtendedValidatorServiceProvider extends ServiceProvider {
 
    public function register(){}
 
    public function boot()
    {
        $this->app->validator->resolver(function($translator, $data, $rules, $messages)
        {
            return new ExtendedValidator($translator, $data, $rules, $messages);
        });
    }
}

Then in app.php, at the end of the providers array:

'providers' => array(
...
'App\Services\Validators\ExtendedValidatorServiceProvider',
),

Run composer dump-autoload -o

(the -o is for faster performance)

Now we can change the validation rule to:

'iban.0' => 'required|alpha_num_spaces'

Array max size

I want to make sure that a hacker/user can submit no more than 3 iban numbers. There is no boilerplate code for that so we have to write it ourselves. I continue with the files I created in the previous steps.

In ExtendedValidator.php:

     public function validateArraySize($attribute, $value, $parameters){
        $data = array_get($this->data, $attribute);
 
        if(!is_array($data)){
            return true;
        }else{
            $sizeIsOk = count($data) <= $parameters[0];
            return $sizeIsOk;
        }
    }

This function will listen to the rule array_size. You can use it like this:

'iban' => 'array|array_size:3'

This makes sure that the iban field is an array, and can only contain 3 keys.

To create a nice error message to the user, go to app/lang/en/validation.php

app/lang/en/validation.php

return array(
...
    "array_size"     => "You can only enter :array_size different values for :attribute.",
);

You might wonder how the system knows what :array_size is. Well, it doesn’t. We have to tell Laravel what it is.

Go to ExtendedValidator.php

Enter the following:

    /**
     * Replace all place-holders for the min rule.
     *
     * @param  string  $message
     * @param  string  $attribute
     * @param  string  $rule
     * @param  array   $parameters
     * @return string
     */
    protected function replaceArraySize($message, $attribute, $rule, $parameters)
    {
        return str_replace(':array_size', $parameters[0], $message);
    }

This will replace :array_size with the value you entered in the validation rules.

Almost there.

I also want the following condition: if a user enters an iban, he also has to enter a bic.

You can use this out of the box working rule:

'bic.0' => 'alpha_num_spaces|required_with:iban.0',

required_if with form arrays

For the real daredevils: what if the user first has to check a box before he can enter the iban?

That makes:

checkbox[]

iban[]

bic[]

checkbox[]

iban[]

bic[]

checkbox[]

iban[]

bic[]

… and other form elements …

To be able to do this we need a multi_required_if that we have to write ourselves. I based it loosely on validationRequiredIf from Validator.php

In ExtendedValidator.php

   /**
     *<!--DVFMTSC--> "Required if" element corresponds in an array
     */
    protected function validateMultiRequiredIf($attribute, $value, $parameters){
 
        $this->requireParameterCount(2, $parameters, 'multi_required_if');
 
        $parameterKey = substr($parameters[0], strpos($parameters[0], '.') + 1);
        $parameterName = substr($parameters[0], 0, strpos($parameters[0], '.'));
 
        $data = array_get($this->data, $parameterName);
 
        if(!is_array($data)){
            return true;
        }
 
        $values = array_slice($parameters, 1);
 
        if (in_array($data[$parameterKey], $values))
        {
            $isEmpty = $this->validateRequired($attribute, $value[$parameterKey]);
            return $isEmpty;
        }
 
        return true;
    }

You can use it like this:

'iban.0' => 'multi_required_if:checkbox-element.0,1',

This means that the first iban textfield (iban.0) must be filled id when the first checkbox element (checkbox-element.0) is checked.

At last, you have to make a rule for each form array element. Too bad I didn’t have the time to figure out how the validation rules can work on each element in the array. With the last example, you have to write a rule for each array element:

'iban.0' => 'multi_required_if:checkbox-element.0,1',

'iban.1' => 'multi_required_if:checkbox-element.1,1',

'iban.2' => 'multi_required_if:checkbox-element.2,1',

Sometimes it can be desirable to remove a database column that hosts a foreign key relationship (eg: in reverse migration scripts). It can be a bit of a hassle to get that done.

Here’s how to do it:

1) Log in to your database and lookup the name of the foreign key relationship. If you use phpmyadmin, go to the table, click the “Structure” tab, click the link “Relation View” and wait a few seconds for it to load. Search for the field “Constraint name”. In my example this is: “contribution_copyright_id_foreign”

2) Go to the Laravel migration script (or create one). The trick is to first drop the foreign key relationship and then drop the column.

 public function down()
 {
        Schema::table('contribution', function(Blueprint $table){
            $table->dropForeign('contribution_copyright_id_foreign');
            $table->dropColumn('copyright_id');
        });

If you want to remove a table where a foreign key is present, you also first have to drop the foreign key relationship.

A quick tip: if you want autocomplete (intellisense) when writing migrations in Laravel, you can add type hinting for the $table variable.

Just add Blueprint before $table in the function argument. Blueprint is the type of the $table variable. You’ll be able to see all the options and don’t have to check Laravel docs anymore.

class Payments extends Migration {
 
  public function up() {
    Schema::create('donor_account', function(Blueprint $table){
      $table->engine ='InnoDB';
      $table->increments('id');

This is how it looks in PHPStorm 8:

phpstorm_table_autocomplete

Composer is a major part of the Laravel MVC Framework, but it also exists without Laravel. In fact you could use it in any project. This article digs into the different files that are used by composer. It’s important to understand what these files are and what these files do.

composer.json

This is the only file you have to edit manually. In this file you can lists which packages you want and which versions of that package you want to install. Versions can be vague (1.x.x) or specific (1.1.2).

vendor/composer/autoload_classmap.php

  • This file (no class) returns an array of all aliasses and files based on the autoload section in composer.json.
  • This file is regenerated on each dump-autoload. If you have a new class somewhere in your project it will not be loaded unless it is included in autoload_classmap (hence you have to execute composer dump-autoload)
  • In Laravel, composer.json includes all controllers, models, commands, migrations, seeds, services and facades in your root folder structure. If you want a custom folder to dump files, you have to add it to the autoload-section in composer.json. That way it will be included in the autoload_classmap.php
  • autoload_classmap.php also includes the providers in config/app.php
  • In Laravel, the autoload_classmap is included inside app/bootstrap/autoload.php (as /../vendor/autoload.php which includes the autoload_classmap)

composer.lock

  • This file is not, as it might suggest, an indication of an update of an install going on. It’s not.
  • composer.lock lists all exact versions of each vendor package that is installed.
  • If you run composer install and there is a lock file present, it will download the versions of composer.lock no matter what’s inside composer.json
  • If you run composer install and there is no lock file, it will generate a lock file of all the vendor versions it has installed based on composer.json
  • If you run composer update it will overwrite the composer.lock file with the newest available vendor packages based on composer.json
  • This means that if you include composer.lock in your GIT repository; clone and execute composer install on another computer, it will download the exact same versions as in composer.lock

What’s the difference between composer dump-autoload, composer update and composer install?

The above text already explains the difference between those commands, but for fast readers:

  • composer install installs the vendor packages according to composer.lock (or creates composer.lock if not present),
  • composer update always regenerates composer.lock and installs the lastest versions of available packages based on composer.json
  • composer dump-autoload won’t download a thing. It just regenerates the list of all classes that need to be included in the project (autoload_classmap.php). Ideal for when you have a new class inside your project.
    • Ideally, you execute composer dump-autoload -o , for a faster load of your webpages. The only reason it is not default, is because it takes a bit longer to generate (but is only slightly noticable)
  • Published:May 7th, 2014
  • Category:Lavarel
  • 8 comments

Mail:send() and Mail:queue() don’t work the same when it comes to passing $data to a view. At least not when you’re passing an eloquent object (or a “model” object).

Eg:

$data = array();
$data['myObject'] = $eloquentObject;
 
Mail::send('emails.hello', $data, function($message) use ($toAddress) {
...
});

This will pass $data to the view so you can access $myObject in the view.

But when you change Mail::send to Mail::queue the $myObject isn’t accessible as expected. This happens when you’re passing an eloquent (inherited) object. To make this work with queue you have to serialize the $eloquentObject first and later unserialize it in the view.

$data = array();
$data['myObject'] = serialize($eloquentObject);
 
Mail::send('emails.hello', $data, function($message) use ($toAddress) {
...
});

In the view:

<?php $serializedObject = unserialize($myObject); ?>
 
{{ $serializedObject->property }}

If you want Laravel to show cached content from Varnish on public pages (so without a cookie), but still want to use a cookie on admin pages, and switch between them, config the following:

Put every admin page on a subdomain: admin.mysite.com

in routes.php add the following:

Route::group(array('domain' => 'admin.mysite.com'), function()
{
//admin routes
}
 
Route::group(array('domain' => 'www.mysite.com'), function()
{
//public routes
}

Set cookieless session for public pages

in app/config/session.php

  • Set ‘driver’ to ‘array’. The option “array” will not write cookies. This is what we want for the public pages.
  • Set ‘cookie’ to a decent name.

Leave everything else default.

Override the session driver for admin pages.

The Laravel Session is initialized at the very beginning of each webserver request. There’s no point in overwriting the session driver in a controller or in a route filter (as strangely suggested on the github) because the session is already loaded and initialized before the route filter kicks in.

To overwrite the session config, you have to edit bootstrap/start.php

In bootstrap/start.php

Right after this line

require $framework.'/Illuminate/Foundation/start.php';

write a code snippet that looks like this:

if(\Request::server('HTTP_HOST') == 'admin.mysite.com'){
    Config::set('session.driver', 'native');
}

By doing this we change the session.driver to “native” (so with a cookie) for the admin pages and not on the public pages.

There is one potential pitfall:

On your admin pages, every asset (css, js, image) must be called from the admin subdomain (except assets from other domains or the cloud).

On your public pages, not a single asset (css, js, image) should be called from the admin subdomain. (so don’t use a “http://admin.mysite.com/images/login.gif” on a www.mysite.com page)

Otherwise, if an assets happens to be a 404 and goes through the webserver, it might conflict or create unwanted cookies.

The above example is a stripped down version of my own implementation. You should care for authentication (I use the Sentry2 package for Laravel). With Sentry and the above setup, you also have to put the login page (or certainly the POST-action) on the admin subdomain. Otherwise the login won’t work (because it will try to write an authentication cookie on the public pages, but can’t because of the “array” session driver, so the user will never be able to login).

There might be other ways to accomplish the same result but this setup definatly works.

Laravel works out of the box with Memcached. However there’s a difference between the linux programs Memcached and Memcache (without the D). To get Laravel to work with Memcache you can write a package yourself.

First of all: don’t edit the original files of the Laravel package. I know you can just find/replace every instance of Memcached and replace it by Memcache, but as soon as you’ll update your project, every change you’ve made to the core files will be overridden and lost. That’s why you have to create a separate package that adds functionality to the system instead of blindly editing the system.

Continue reading “Laravel use Memcache instead of MemcacheD”…

It felt like a Monday morning. After my alarm clock didn’t get off (+ 1 hour), I noticed there was only a train 1 hour later, so instead of arriving at half past 9, I arrived at 11 and missed the first speaker.

Lightswith + Drupal

Anyhow. I picked up the last quarter of using Drupal together with Lightswitch (= Visual Studio). Apparently not a lot of devs had interest in the subject because there were only 20 people in the room. And those who didn’t attend were right, because all the speaker could tell us was that marrying Drupal and Lightswith could only result in a divorce.

Lightswitch can create a HTML5-admin environment based on a data layer. That data layer could be your Drupal database. Nothing works out the box for Drupal because MS of course wants to integrate their software (SharePoint, Office) and not someone else’s software.

Another downsides of all these MS-Drag-And-Drop-Automatic-Data-Layer-Builder-stuff, is that when you change your database, something on the other side might break and you could end up writing the data layer yourself (as a attendee commented). Plus, the actual html output looks weak and is unusable in a serious professional environment. Don’t try this at work, pro’s!

Drupal 8 discussion panel

Three Belgian core-devs (swentel, Wim Leers, aspilicious) had a one hour Q&A hour about Drupal 8. They all had a lot to tell so the number of actual public questions was limited.

You had to know some Drupal 8 in forehand, because new projects (say WSCCI, PSR or TWIG) were discussed without being explained.

The main message was that Drupal 8 is ready to port your modules to. But, there’s still a lot of work to be done. There are still upcoming API-changes, you can’t translate a node’s title yet and there are various other big and small release blockers. But: Views should be finished. Ah!

And why Drupal 8 should be better that its processors:

  • PSR proof. PSR is a PHP coding standard. (aimed for PSR 4 however, it’s uncertain if it the project will get there)
  • Display Suite is now a core module (however, is this really such a big plus?)
  • Getting rid of the hooks in favor for a more object oriented way (however, hooks still exist)

Continue reading “DrupalCamp Leuven 2013, a brief Saturday review.”…

What’s a pivot table?

A pivot table is a database table that only exists to serve a many-to-many relationship. Say you have a table “customer” and a table “drinks”. If you want to know which customer ordered which drink you have to create a pivot table customer_drinks(customer_id, drink_id).

Laravel can handle these tables (semi)automatically.

Define the pivot table in Laravel

If you want to define the pivot table in Laravel, you have to create a belongsToMany relationship. Example:

Continue reading “Laravel 4: pivot table example (attach and detach)”…

Next Page »