Skip to content

Commit a0199fd

Browse files
committed
Added attribute casting
1 parent 00ed7d4 commit a0199fd

File tree

6 files changed

+223
-31
lines changed

6 files changed

+223
-31
lines changed

readme.md

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,31 @@ $post = $this->shared('post');
148148
$allShared = $this->shared();
149149
```
150150

151+
### Attribute casting
152+
153+
The $casts property on your shortcode class provides a convenient method of converting attributes to
154+
common data types. The $casts property should be an array where the key is the name of the attribute
155+
being cast and the value is the type you wish to cast the column to. The supported cast types are:
156+
`int`, `integer`, `real`, `float`, `double`, `boolean`, `array` (comma separated values) and `date`.
157+
158+
```blade
159+
class YourShortcode extends Shortcode
160+
{
161+
/**
162+
* The attributes that should be cast to native types.
163+
*
164+
* @var array
165+
*/
166+
protected $casts = [
167+
'show_ids' => 'array',
168+
];
169+
}
170+
```
171+
172+
Now the `show_ids` attribute will always be cast to an array when you access it.
173+
(array attributes are casted from comma separated string, eg. "1,2,3").
174+
175+
151176
### Option to not throw exceptions from views
152177

153178
There is a useful option to aviod server (500) error for whole page when one of shortocode views has thrown an exception.
@@ -159,19 +184,6 @@ This will render exception details in the place of a shortcode and will not cras
159184
It will still log exception to a log file and report to [Sentry](https://sentry.io/) if it's integrated.
160185

161186

162-
### Comma separated values (array attributes)
163-
164-
If you need to pass an array to a shortcode, you can pass values separated by comma:
165-
166-
```blade
167-
[posts_list ids="1,2,3"]
168-
```
169-
170-
Then in the `render` function you can parse this attribute using built-in method:
171-
```php
172-
$ids = $this->parseCommaSeparated($atts['ids']);
173-
```
174-
175187
### Generate data for documentation
176188

177189
There can be hundreds of registered shortcodes and having a way to show documentation for all
@@ -199,11 +211,9 @@ $ vendor/bin/phpunit
199211

200212
## TODO
201213

202-
1. Casting attributes (int, bool, array (comma separated))
203-
1. Add basic bootstrap shortcodes set
204214
1. Attributes validation
205-
1. Add Debugbar integration tests
206215
1. Add custom widget for debugbar integration
216+
1. Add Debugbar integration tests
207217
1. Create performance profile tests, optimize performance
208218

209219
## Contributing

src/Renderer.php

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
namespace Vedmant\LaravelShortcodes;
44

55
use Exception;
6-
use Illuminate\Support\Traits\Macroable;
76
use Illuminate\Contracts\Foundation\Application;
7+
use Illuminate\Support\Traits\Macroable;
88

99
class Renderer
1010
{
@@ -110,24 +110,24 @@ private function doShortcodeTag($m)
110110

111111
$tag = $m[2];
112112
$atts = $this->shortcodeParseAtts($m[3]);
113-
$shortcode = null;
114-
115113
/* @var Shortcode $shortcode */
116-
if (is_callable($this->shortcodes[$tag])) {
117-
$content = $m[1].$this->shortcodes[$tag]($atts, isset($m[5]) ? $m[5] : null, $tag,
118-
$this->manager).$m[6];
119-
} elseif (class_exists($this->shortcodes[$tag])) {
120-
$shortcode = new $this->shortcodes[$tag]($this->app, $this->manager, (array) $atts, $tag);
121-
if (! $shortcode instanceof Shortcode) {
122-
$content = "Class {$this->shortcodes[$tag]} is not an instance of ".Shortcode::class;
114+
$shortcode = $this->shortcodes[$tag];
115+
$instance = null;
116+
117+
if (is_callable($shortcode)) {
118+
$content = $m[1] . $shortcode($atts, isset($m[5]) ? $m[5] : null, $tag, $this->manager) . $m[6];
119+
} elseif (class_exists($shortcode)) {
120+
$instance = new $shortcode($this->app, $this->manager, (array) $atts, $tag);
121+
if (! $instance instanceof Shortcode) {
122+
$content = "Class {$shortcode} is not an instance of " . Shortcode::class;
123123
} else {
124-
$content = $m[1].$shortcode->render(isset($m[5]) ? $m[5] : null).$m[6];
124+
$content = $m[1] . $instance->render(isset($m[5]) ? $m[5] : null) . $m[6];
125125
}
126126
} else {
127-
$content = "Class {$this->shortcodes[$tag]} doesn't exists";
127+
$content = "Class {$shortcode} doesn't exists";
128128
}
129129

130-
$this->shortcodeDone($tag, $shortcode, microtime(true) - $startTime);
130+
$this->shortcodeDone($tag, $instance, microtime(true) - $startTime);
131131

132132
return $content;
133133
}

src/Shortcode.php

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Vedmant\LaravelShortcodes;
44

5+
use Carbon\Carbon;
56
use Throwable;
67
use Illuminate\Support\Collection;
78
use Illuminate\Support\Facades\Log;
@@ -37,6 +38,13 @@ abstract class Shortcode implements ShortcodeContract
3738
*/
3839
protected $atts;
3940

41+
/**
42+
* The attributes that should be cast to native types.
43+
*
44+
* @var array
45+
*/
46+
protected $casts = [];
47+
4048
/**
4149
* @var string Rendered tag name
4250
*/
@@ -54,7 +62,7 @@ public function __construct(Application $app, Manager $manager, array $atts, $ta
5462
{
5563
$this->app = $app;
5664
$this->manager = $manager;
57-
$this->atts = $atts;
65+
$this->atts = $this->castAttributes($atts);
5866
$this->tag = $tag;
5967
}
6068

@@ -145,6 +153,56 @@ protected function view($name, $data = [])
145153
}
146154
}
147155

156+
/**
157+
* Cast attributes
158+
*
159+
* @param $atts
160+
* @return array
161+
*/
162+
protected function castAttributes($atts)
163+
{
164+
if (! $this->casts) {
165+
return $atts;
166+
}
167+
168+
foreach ($atts as $key => $value) {
169+
$atts[$key] = $this->castAttribute($key, $value);
170+
}
171+
172+
return $atts;
173+
}
174+
175+
/**
176+
* Cast an attribute to a native PHP type.
177+
*
178+
* @param string $key
179+
* @param mixed $value
180+
* @return mixed
181+
*/
182+
protected function castAttribute($key, $value)
183+
{
184+
switch (array_get($this->casts, $key)) {
185+
case 'int':
186+
case 'integer':
187+
return (int) $value;
188+
case 'real':
189+
case 'float':
190+
case 'double':
191+
return (float) $value;
192+
case 'bool':
193+
case 'boolean':
194+
return $value === 'true';
195+
case 'array':
196+
return $this->parseCommaSeparated($value);
197+
case 'json':
198+
return json_decode($value, true);
199+
case 'date':
200+
return Carbon::parse($value);
201+
default:
202+
return $value;
203+
}
204+
}
205+
148206
/**
149207
* Parse comma separated values.
150208
*

src/Shortcodes/BShortcode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class BShortcode extends Shortcode
2929
public function render($content)
3030
{
3131
return $this->view('shortcodes::b', [
32-
'atts' => $this->atts,
32+
'atts' => $this->atts(),
3333
'content' => $content,
3434
]);
3535
}

tests/Resources/CastsShortcode.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace Vedmant\LaravelShortcodes\Tests\Resources;
4+
5+
use Vedmant\LaravelShortcodes\Shortcode;
6+
7+
class CastsShortcode extends Shortcode
8+
{
9+
/**
10+
* @var string Shortcode description
11+
*/
12+
public $description = 'List of casts';
13+
14+
/**
15+
* @var array Shortcode attributes with default values
16+
*/
17+
public $attributes = [
18+
'string' => [
19+
'default' => ''
20+
],
21+
'int' => [
22+
'default' => ''
23+
],
24+
'integer' => [
25+
'default' => '',
26+
],
27+
'real' => [
28+
'default' => '',
29+
],
30+
'float' => [
31+
'default' => '',
32+
],
33+
'double' => [
34+
'default' => '',
35+
],
36+
'bool' => [
37+
'default' => '',
38+
],
39+
'boolean' => [
40+
'default' => '',
41+
],
42+
'array' => [
43+
'default' => '',
44+
],
45+
'json' => [
46+
'default' => '',
47+
],
48+
'date' => [
49+
'default' => '',
50+
],
51+
];
52+
53+
/**
54+
* The attributes that should be cast to native types.
55+
*
56+
* @var array
57+
*/
58+
protected $casts = [
59+
'int' => 'int',
60+
'integer' => 'integer',
61+
'real' => 'real',
62+
'float' => 'float',
63+
'double' => 'double',
64+
'bool' => 'bool',
65+
'boolean' => 'boolean',
66+
'array' => 'array',
67+
'json' => 'json',
68+
'date' => 'date',
69+
];
70+
71+
/**
72+
* Render shortcode.
73+
*
74+
* @param string $content
75+
* @return string
76+
*/
77+
public function render($content)
78+
{
79+
$atts = $this->atts();
80+
81+
return "<b class=\"{$atts['class']}\">{$content}</b>";
82+
}
83+
}

tests/Unit/ShortcodeTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Vedmant\LaravelShortcodes\Tests\Unit;
4+
5+
use Carbon\Carbon;
6+
use Vedmant\LaravelShortcodes\Tests\Resources\CastsShortcode;
7+
use Vedmant\LaravelShortcodes\Tests\TestCase;
8+
9+
class ShortcodeTest extends TestCase
10+
{
11+
public function testCasts()
12+
{
13+
$shortcode = new CastsShortcode($this->app, $this->manager, [
14+
'string' => 'string',
15+
'int' => '123',
16+
'integer' => '123',
17+
'real' => '12.34',
18+
'float' => '12.34',
19+
'double' => '12.34',
20+
'bool' => 'true',
21+
'boolean' => 'false',
22+
'array' => '1,2,3,4',
23+
'json' => '{"test": 123}',
24+
'date' => '2019-01-01',
25+
], 'casts');
26+
27+
$atts = $shortcode->atts();
28+
29+
$this->assertIsString($atts['string']);
30+
$this->assertIsInt($atts['int']);
31+
$this->assertIsInt($atts['integer']);
32+
$this->assertIsFloat($atts['real']);
33+
$this->assertIsFloat($atts['float']);
34+
$this->assertIsFloat($atts['double']);
35+
$this->assertIsBool($atts['bool']);
36+
$this->assertIsBool($atts['boolean']);
37+
$this->assertIsArray($atts['array']);
38+
$this->assertIsArray($atts['json']);
39+
$this->assertInstanceOf(Carbon::class, $atts['date']);
40+
}
41+
}

0 commit comments

Comments
 (0)