Skip to content

Commit 8f7fbb7

Browse files
committed
Initial commit
0 parents  commit 8f7fbb7

16 files changed

+745
-0
lines changed

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#
2+
# OS generated files and junk
3+
#
4+
5+
.DS_Store
6+
.DS_Store?
7+
._*
8+
Thumbs.db
9+
Icon?
10+
.Trashes
11+
ehthumbs.db
12+
*.log
13+
14+
#
15+
# PhpStorm
16+
#
17+
18+
.idea
19+
20+
#
21+
# Project
22+
#
23+
24+
vendor
25+
composer.lock

.travis.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
language: php
2+
3+
php:
4+
- 7.0
5+
- 7.1
6+
- 7.2
7+
8+
before_script:
9+
- composer self-update
10+
- composer install --prefer-source --no-interaction
11+
- composer dump-autoload
12+
13+
script:
14+
- vendor/bin/phpunit

LICENCE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2018 Movor <https://movor.io>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Laravel custom casts for models
2+
3+
[![Build](https://travis-ci.org/movor/laravel-custom-casts.svg?branch=master)](https://travis-ci.org/movor/laravel-custom-casts)
4+
[![Downloads](https://poser.pugx.org/movor/laravel-custom-casts/downloads)](https://packagist.org/packages/movor/laravel-custom-casts)
5+
[![Stable](https://poser.pugx.org/movor/laravel-custom-casts/v/stable)](https://packagist.org/packages/movor/laravel-custom-casts)
6+
[![License](https://poser.pugx.org/movor/laravel-custom-casts/license)](https://packagist.org/packages/movor/laravel-custom-casts)
7+
8+
Make your own custom cast for Laravel model.
9+
10+
***
11+
12+
## Compatibility
13+
14+
The package is compatible with Laravel versions 5.5.* and 5.6.*

composer.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "movor/laravel-custom-casts",
3+
"description": "Make your own custom cast for Laravel model",
4+
"keywords": [
5+
"laravel",
6+
"model",
7+
"casts",
8+
"cast",
9+
"datatype"
10+
],
11+
"license": "MIT",
12+
"authors": [
13+
{
14+
"name": "Vladimir Ković",
15+
"email": "vlada.kovic@gmail.com"
16+
}
17+
],
18+
"autoload": {
19+
"psr-4": {
20+
"Movor\\LaravelCustomCasts\\": "src/package"
21+
}
22+
},
23+
"autoload-dev": {
24+
"psr-4": {
25+
"Movor\\LaravelCustomCasts\\Test\\": "tests"
26+
}
27+
},
28+
"require": {
29+
"laravel/framework": "5.5.*|5.6.*"
30+
},
31+
"require-dev": {
32+
"orchestra/testbench": "3.5.*|3.6.*",
33+
"orchestra/database": "3.5.*|3.6.*"
34+
},
35+
"scripts": {
36+
"test": "vendor/bin/phpunit"
37+
}
38+
}

phpunit.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit bootstrap="vendor/autoload.php"
3+
backupGlobals="false"
4+
backupStaticAttributes="false"
5+
colors="true"
6+
verbose="true"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
processIsolation="false"
11+
stopOnFailure="false">
12+
<testsuites>
13+
<testsuite name="LaravelCustomCasts Test Suite">
14+
<directory>tests</directory>
15+
</testsuite>
16+
</testsuites>
17+
<filter>
18+
<whitelist>
19+
<directory suffix=".php">src/</directory>
20+
</whitelist>
21+
</filter>
22+
</phpunit>

src/package/CustomCastableBase.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Movor\LaravelCustomCasts;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
abstract class CustomCastableBase
8+
{
9+
/**
10+
* Model
11+
*
12+
* @var Model
13+
*/
14+
protected $model;
15+
16+
/**
17+
* Corresponding db field (model attribute name)
18+
*
19+
* @var string
20+
*/
21+
protected $attribute;
22+
23+
/**
24+
* Set model
25+
*
26+
* @param Model $model
27+
*/
28+
public function setModel(Model $model)
29+
{
30+
$this->model = $model;
31+
}
32+
33+
/**
34+
* Set model field name which is going to be custom casted
35+
*
36+
* @param $attribute
37+
*/
38+
public function setAttribute($attribute)
39+
{
40+
$this->attribute = $attribute;
41+
}
42+
43+
/**
44+
* Enforce implementation in child classes
45+
*
46+
* Intercept value passed to model under specified field ($attribute)
47+
* and change it to our will
48+
*
49+
* @param mixed $value Default value passed to model attribute
50+
*
51+
* @return mixed
52+
*/
53+
abstract public function castAttribute($value);
54+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
namespace Movor\LaravelCustomCasts;
4+
5+
trait CustomCastableTrait
6+
{
7+
/**
8+
* Each field which is going to be custom casted
9+
* will have its own custom cast instance in this array
10+
*
11+
* @var array
12+
*/
13+
protected $customCastObjects = [];
14+
15+
/**
16+
* Boot trait
17+
*/
18+
public static function bootCustomCastableTrait()
19+
{
20+
// Enable custom cast classes to listen to model events
21+
\Event::listen('eloquent.*: ' . get_called_class(), function ($event, $data) {
22+
$eventName = explode('.', explode(':', $event)[0])[1];
23+
24+
/** @var self $model */
25+
$model = $data[0];
26+
27+
if (!isset($model->customCasts)) {
28+
self::throwInvalidCustomCastException($model);
29+
}
30+
31+
foreach ($model->customCasts as $key => $value) {
32+
$customCastObject = $model->getCustomCastObject($key);
33+
34+
if (method_exists($customCastObject, $eventName)) {
35+
$customCastObject->$eventName();
36+
}
37+
}
38+
});
39+
}
40+
41+
/**
42+
* Hook into setAttribute logic and enable our custom cast do the job.
43+
*
44+
* This method is will override method in HasAttributes trait.
45+
*
46+
* @param $attribute
47+
* @param $value
48+
*
49+
* @throws \Exception
50+
*
51+
* @return mixed
52+
*/
53+
public function setAttribute($attribute, $value)
54+
{
55+
if (// Handle defined mutators in object and
56+
// prioritize them against custom castable
57+
!$this->hasSetMutator($attribute) &&
58+
59+
// Check if there is custom casts for this attribute
60+
isset($this->customCasts) && array_key_exists($attribute, $this->customCasts)
61+
) {
62+
$customCastObject = $this->getCustomCastObject($attribute);
63+
64+
// Cast attribute according to logic from custom cast object
65+
$this->attributes[$attribute] = $customCastObject->castAttribute($value);
66+
67+
return $this;
68+
}
69+
70+
return parent::setAttribute($attribute, $value);
71+
}
72+
73+
/**
74+
* Lazy load custom cast object and return it
75+
*
76+
* @param $attribute
77+
*
78+
* @throws \Exception
79+
*
80+
* @return CustomCastableBase
81+
*/
82+
protected function getCustomCastObject($attribute)
83+
{
84+
// Check if custom cast object already been set
85+
if (isset($this->customCastObjects[$attribute])) {
86+
return $this->customCastObjects[$attribute];
87+
}
88+
89+
// Check if custom casts property exists for this attribute
90+
if (isset($this->customCasts) && array_key_exists($attribute, $this->customCasts)) {
91+
$customCastClass = $this->customCasts[$attribute];
92+
93+
// Make sure that defined custom cast class is correct
94+
if (!is_subclass_of($customCastClass, CustomCastableBase::class)) {
95+
$message = "Custom cast class for '$attribute' needs to be extended from ";
96+
$message .= CustomCastableBase::class;
97+
98+
throw new \Exception($message);
99+
}
100+
101+
$customCastObject = new $customCastClass($this, $attribute);
102+
return $this->customCastObjects[$attribute] = $customCastObject;
103+
}
104+
105+
self::throwInvalidCustomCastException($this);
106+
}
107+
108+
/**
109+
* @param $model
110+
*
111+
* @throws \Exception
112+
*/
113+
public static function throwInvalidCustomCastException($model)
114+
{
115+
$trait = CustomCastableTrait::class;
116+
$model = self::class;
117+
$message = "Model class '$model' which uses '$trait' needs to have 'customCast' array property defined";
118+
119+
throw new \Exception($message);
120+
}
121+
}

0 commit comments

Comments
 (0)