mdcass/support

A set of helpers and classes to aid with package development within Laravel. Inspired by Laravel's Eloquent Model functionality amongst other things.

v3.0 2017-01-12 20:51 UTC

This package is not auto-updated.

Last update: 2020-11-08 18:38:56 UTC


README

Overview

A set of helpers and classes to aid with package development within Laravel. Inspired by Laravel's Eloquent Model functionality amongst other things.

  1. Installation
  2. Base Class
  1. Mocking Excel Files
  2. Contributing

Installation

Installation instructions are for Laravel 5.3

Require the library

composer require mdcass/support

Add the Service Provider to the providers list in config/app.php

// config/app.php

'providers' => [
    ...
    /*
     * Third Party Service Providers
     */
    Mdcass\Support\SupportServiceProvider::class
]

Base Class

Use the base class by extending your own class to add richer functionality (much mimcked from the Eloquent model, but more lightweight)

Setting Attributes

class Example extends Mdcass\Support\Base {}

Setting attributes is standard, however none existent attributes will return null instead of throwing an exception

$class = new Example();
$class->new_attribute = 'Hello there.';

echo $class->new_attribute; // Hello there.
echo $class->non_existent_attribute; // null

Filling Attributes

You may mass assign attributes on a class either when instantiating, or later on

$class = new Example(['numeric_value' => 1, 'array_value' => ['hi', 'there']]);
$class->numeric_value; // 1

$newClass = new Example;
$newClass->fill(['numeric_value' => 1, 'array_value' => ['hi', 'there']]);
$newClass->array_value; // ['hi', 'there']

Attributes By Array Path

The class with conveniently get and set array values on attributes where the key is a period . delimited path

$class = new Example;
$class->{'path.to.attribute'} = 'hello';

$class->toArray(); // ['path' => ['to' => ['attribute' => 'hello']]];
$class->path['to']; // ['attribute' => 'hello']
$class->{'path.to.attribute'}; // hello

Beware that this functionality cannot currently be toggled, meaning you should not use periods . in your attribute keys unless you want this functionality

toArray Method

For debugging, the class comes with a toArray method

$class = new Example(['numeric_value' => 1, 'array_value' => ['hi', 'there']);
$class->another_attribute = 'hello';

$class->toArray(); // ['numeric_value' => 1, 'array_value' => ['hi', 'there'], 'another_attribute' => 'hello']

If an attribute is an object which also implements the Base class, it's toArray method will be called in a recursive fashion

Set By Chainable Method

You may define attributes which can be set by a chainable method. List the attribute names in the $set_by_method property on the class. Attributes should be snake_case whereas the method being called would be camel_case.

/**
 * @property string $connection
 * @property string $db_table
 */
class MethodSettingExample extends Mdcass\Support\Base {
    protected $set_by_method = [
        'connection',
        'db_table',
    ];
}

$class = new MethodSettingExample;
$class->connection('mysql')->dbTable('tbl_name');

$class->toArray(); // ['connection' => 'mysql', 'db_table' => 'tbl_name']

Any mutators defined for the attribute will be applied

Accessors And Mutators

Accessors and Mutators allow you to alter attributes when getting or setting them. This functionality is similar to Laravel's Eloquent functionality.

Defining An Accessor

To define an accessor, create a getFooAttribute method on your model where Foo is the "studly" cased name of the attribute. The stored value is passed to the accessor:

class AccessorExample extends Mdcass\Support\Base {
    /**
     * Get the first name attribute
     *
     * @param  string  $value
     * @return string
     */
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }
}

$class = new AccessorExample;
$class->first_name = 'mike';
echo $class->first_name; // Mike

Accessors are applied when using the toArray method

Defining A Mutator

To define a mutator, create a setFooAttribute method on your model where Foo is the "studly" cased name of the attribute. This will be called when setting the value. Note that you should set the value on the attributes property of the class:

class MutatorExample extends Mdcass\Support\Base {
    /**
     * Multiply the value by 10 when setting
     *
     * @param integer
     */
    public function setMultiplierAttribute($value)
    {
        $this->attributes['multiplier'] = $value * 10;
    }
}

$class = new MutatorExample;
$class->multiplier = 1;
echo $class->multiplier; // 10

Attribute Casting

You can have the model convert attributes to common data types by listing them in the $casts property, where the key is the name of the attribute and the value is the type: integer, float, string, boolean, date, timestamp. A Carbon instance is returned for Dates.

class CastingExample extends Mdcass\Support\Base {
    protected $casts = [
        'is_boolean' => 'boolean',
    ];
}

$class = new CastingExample;
$class->is_boolean = 1;
$class->is_boolean // true

Set a default value by either filling the class on instantiation, or in the $attributes property:

class CastingExample extends Mdcass\Support\Base {
    protected $attributes = [
        'some_date' => '2016-01-03'
    ];
    protected $casts = [
        'some_date' => 'date',
    ];
}

$class = new CastingExample;
$class->some_date // Carbon class
$class->some_date->toDateString() // 2016-01-03

Collecting Aggregate Values

You can use the Traits\AggregateAttributes trait to have attributes collect an aggregate of the values set on it. Use the trait along with an $aggregate_attributes array property which lists the attriutes (as the key) and the type of aggregates. The available types are distinct, sum, and min_max.

class AggregateExample extends Mdcass\Support\Base
{
    use Mdcass\Support\Traits\AggregateAttributes;

    protected $aggregate_attributes = [
        'i_am_distinct'    => 'distinct',
        'i_will_be_summed' => 'sum',
        'i_will_be_minmax' => 'min_max'
    ];
}

$class = new AggregateTestModel;

$class->i_am_distinct = 'a';
$class->i_am_distinct = 'b';
$class->i_am_distinct = 'a';

var_dump($class->i_am_distinct); // ['a', 'b']

$class->i_will_be_summed = 1;
$class->i_will_be_summed = 2;
$class->i_will_be_summed = 3;

var_dump($class->i_will_be_summed); // 6

$class->i_will_be_minmax = 1;
$class->i_will_be_minmax = 2;
$class->i_will_be_minmax = 3;

var_dump($class->i_will_be_minmax); // ['min' => 1, 'max' => 3]

Attribute Validation

You can have the class automatically validate an attribute value when it is set using the Respect/Validation library. An exception will be thrown if the validation fails.

Use the Traits\Validation trait on your class. Use the $rules property to list the attributes (as the key) then a pipe delimited string of validation rules. The following example works with the fill method too.

class ValidationExample extends Mdcass\Support\Base {

    use Mdcass\Support\Traits\Validation;

    protected $rules = [
        'numeric_attr'     => 'numeric',
        'boolean_attr'     => 'not_false_val'
    ];
}

$class = new ValidationExample;

try {
    $class->numeric_attr = 'string';
} catch(Respect\Validation\Exceptions\NestedValidationException $exception) {
    var_dump($exception->getMessages()); // ['"string" must be numeric']
}

The value under validation is the value taken after attribute casting and accessors have been applied

You can make the validation passive (not thrown an exception) and instead use the isValid() method, along with the errors() method to fetch messages. Simply set the $active_validation property to false

class ValidationExample extends Mdcass\Support\Base {

    use Mdcass\Support\Traits\Validation;

    protected $active_validation = false;

    protected $rules = [
        'numeric_attr'      => 'required|numeric',
        'in_attr'           => 'in:a,b,c'
    ];
}

$class = new ValidationExample;
$class->fill(['numeric_attr' => 'string', 'in_attr' => 'b']);

if(!$class->isValid())
    var_dump($class->classValidationMessages()); // ['"string" must be numeric']

Required Attributes

You must add the required rule to the beginning of the rule string should an attribute be required - otherwise, a null or empty value will pass validation. The first Validation example class above doesn't have the required rule whereas the second does - the first class's numeric_attr would validate as true if the value was null or empty, but will validate the other rules if a value is set.

Negating Attributes

Prepend a rule with not_ to negate a rule, an example of which is in the first example above not_false_val.

Rules

The full rule reference can be found in the Respect/Validation documentation. Where rules in their documentation are camelCase, snake_case is used for this class (to ensure consistency with options like appending not_ to a rule).

Mocking Excel Files

The package comes with a shorthand function for creating Excel files. The target storage path is set in the Laravel-publishable config support-testing.php (which defaults to the parent Laravel installation's storage_path).

The columns are Faker methods which populate the rows.

file_faker('test.xls', [
    [
        [
            'name'      => 'First Sheet',
            'columns'   => ['date', 'company', 'catchPhrase', 'email', 'randomDigit', 'randomFloat'],
            'rows'      => 10
        ],
        [
            'name'      => 'Second Sheet',
            'columns'   => ['date', 'company', 'catchPhrase', 'email', 'randomDigit', 'randomFloat'],
            'rows'      => 10
        ],
        [
            'name'      => 'Empty Sheet',
            'columns'   => ['date', 'company', 'catchPhrase', 'email', 'randomDigit', 'randomFloat'],
            'rows'      => 0
        ]
    ]
]);

This section of the documentation is to be developed as tests have greater coverage

Contributing

Testing

You must add the following package to the parent Laravel application for testing:

$ composer require vladahejda/phpunit-assert-exception --dev

Package testing follows this article. To test:

$ cd /path/to/package/mdcass/file-validator
$ path/to/parent-laravel-app/vendor/bin/phpunit