Skip to content

Commit

Permalink
Merge pull request #23 from samrap/0.4
Browse files Browse the repository at this point in the history
Version 0.4
  • Loading branch information
Sam Rapaport authored Feb 1, 2018
2 parents cccbbff + bea706a commit 55af756
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 26 deletions.
14 changes: 14 additions & 0 deletions docs/02-builder-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@ $content = Acf::field('my_textarea')

If the field value is not a string, a `\Samrap\Acf\Exceptions\RunnerException` exception will be thrown.

#### `Samrap\Acf\Fluent\Builder::matches(string $regex)`

The value must match given `$regex` string:

```php
use Samrap\Acf\Acf;

$field = Acf::field('quote')
->matches('/death|taxes/')
->get();
```

In the above example, the quote _"Nothing is certain but death and taxes."_ would pass.

#### `Samrap\Acf\Fluent\Builder::raw(void)`

When retrieving a field, ACF lets you specify whether or not to format the value from the database. ACF Fluent follows the plugin's convention by formatting the field by default. You may use the builder's `raw` method to retrieve the value from the database unformatted:
Expand Down
2 changes: 1 addition & 1 deletion docs/03-updating-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ Acf::subField('content')
->update('Nothing is certain but death and taxes.');
```

← [Builder Methods](02-builder-methods.md)
← [Builder Methods](02-builder-methods.md) | [Macros](04-macros.md) →
53 changes: 53 additions & 0 deletions docs/04-macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Macros

You may have a chain of methods that are called numerous times throughout your template. For example, imagine you have a repeater field that you loop through in your template and thus need to ensure that the result is always an array. Without ACF Fluent, that might look something like this:

```php
<?php

$items = get_field('items');

?>

<?php if (is_array($items)): ?>
<?php foreach ($items as $item): ?>
<!-- Some HTML Markup -->
<?php endforeach; ?>
<?php endif; ?>

```

As you know, we can use ACF Fluent to clean this up a bit:

```php
<?php foreach (Acf::field('items')->expect('array')->default([])->get() as $item): ?>
<!-- Some HTML Markup -->
<?php endforeach; ?>
```

This is nice, but can get a bit cumbersome to retype every single time we need to loop over a repeater field. ACF Macros allow you to chain together existing methods into one single method that can be called on the builder.

First, define the Macro statically on the `Acf` object (this should be done upon setup, like in `functions.php`):

```php
use Samrap\Acf\Acf;
use Samrap\Acf\Fluent\Builder;

Acf::macro('repeater', function (Builder $builder) {
$builder
->expect('array')
->default([]);
});
```

Now, we can call `repeater` as a method of the Fluent Builder anywhere in our code:

```php
<?php foreach (Acf::field('items')->repeater()->get() as $item): ?>
<!-- Some HTML Markup -->
<?php endforeach; ?>
```

As you can see, this is a nice shortcut to making complex builder calls. With all the [Builder Methods](02-builder-methods.md) available, you can imaging that macros are a good option to further organize your templates.

&larr; [Updating Fields](03-updating-fields.md)
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<p align="center">
<a href="#"><img src="https://img.shields.io/travis/samrap/acf-fluent/master.svg?style=flat-square" alt="Build Status" /></a>
<a href="https://github.com/samrap/acf-fluent/issues"><img src="https://img.shields.io/github/issues/samrap/acf-fluent.svg?style=flat-square" alt="Open Issues" /></a>
<a href="https://scrutinizer-ci.com/g/samrap/acf-fluent/?branch=master"><img src="https://img.shields.io/scrutinizer/g/samrap/acf-fluent.svg?style=flat-square" alt="Code Quality" /></a>
<a href="https://packagist.org/packages/samrap/acf-fluent"><img src="https://img.shields.io/packagist/v/samrap/acf-fluent.svg?style=flat-square" alt="Packagist Version" /></a>
<a href="#"><img src="https://img.shields.io/github/license/samrap/acf-fluent.svg?style=flat-square" alt="MIT License" /></a>
</p>
Expand Down Expand Up @@ -60,6 +60,7 @@ Interested? ACF Fluent packs a lot more features and has **no** dependencies. [C
- [Basic Usage](docs/01-basic-usage.md)
- [Builder Methods](docs/02-builder-methods.md)
- [Updating Fields](docs/03-updating-fields.md)
- [Macros](docs/04-macros.md)

### Contributing

Expand Down
21 changes: 20 additions & 1 deletion src/Acf.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ class Acf
*/
private $behaviors = [];

/**
* The available macros defined.
*
* @var array
*/
private $macros = [];

/**
* Private constructor to prevent instantiation.
*
Expand Down Expand Up @@ -89,6 +96,18 @@ public static function option($name)
->id('option');
}

/**
* Add a macro to the ACF builder.
*
* @param string $method
* @param callable $operation
* @return void
*/
public static function macro($method, $operation)
{
self::getInstance()->macros[$method] = $operation;
}

/**
* Return a builder instance with the given behavior.
*
Expand All @@ -102,6 +121,6 @@ private function getBuilder($behavior)
$this->behaviors[$behavior] = new $behavior();
}

return new Builder(new Runner($this->behaviors[$behavior]));
return new Builder(new Runner($this->behaviors[$behavior]), $this->macros);
}
}
57 changes: 53 additions & 4 deletions src/Fluent/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Samrap\Acf\Fluent;

use BadMethodCallException;
use Samrap\Acf\Exceptions\BuilderException;

class Builder
Expand All @@ -13,6 +14,13 @@ class Builder
*/
protected $runner;

/**
* An array of functions callable as methods of this class.
*
* @var array
*/
protected $macros;

/**
* The field name or key to build off of.
*
Expand Down Expand Up @@ -63,14 +71,22 @@ class Builder
*/
public $raw = false;

/**
* The regular expression that the field's value must match.
*
* @var string
*/
public $matches;

/**
* Create a new Builder instance.
*
* @param \Samrap\Acf\Fluent\Runner $runner
*/
public function __construct(Runner $runner)
public function __construct(Runner $runner, array $macros = [])
{
$this->runner = $runner;
$this->macros = $macros;
}

/**
Expand Down Expand Up @@ -138,7 +154,7 @@ public function default($default)
/**
* Set the escape component.
*
* @param string $func
* @param callable $func
* @return \Samrap\Acf\Fluent\Builder
*/
public function escape($func = 'esc_html')
Expand Down Expand Up @@ -173,6 +189,19 @@ public function raw()
return $this;
}

/**
* Set the matches component.
*
* @param string $pattern
* @return \Samrap\Acf\Fluent\Builder
*/
public function matches($pattern)
{
$this->matches = $pattern;

return $this;
}

/**
* Pass the builder to the runner's get method.
*
Expand All @@ -184,7 +213,7 @@ public function get()
throw new BuilderException('Cannot get a null field.');
}

return $this->runner->runGet($this);
return $this->runner->get($this);
}

/**
Expand All @@ -195,6 +224,26 @@ public function get()
*/
public function update($value)
{
return $this->runner->runUpdate($this, $value);
return $this->runner->update($this, $value);
}

/**
* Call a macro if it exists.
*
* @param string $name
* @param array $arguments
* @return \Samrap\Acf\Fluent\Builder
*/
public function __call($name, $arguments)
{
if (! isset($this->macros[$name])) {
throw new BadMethodCallException(
"The method or macro {$name} does not exist."
);
}

$this->macros[$name]($this, ...$arguments);

return $this;
}
}
19 changes: 16 additions & 3 deletions src/Fluent/Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Runner
*/
protected $components = [
'expect',
'matches',
'default',
'shortcodes',
'escape',
Expand Down Expand Up @@ -66,7 +67,7 @@ public function setBehavior(BehaviorInterface $behavior)
* @param \Samrap\Acf\Fluent\Builder $builder
* @return mixed
*/
public function runGet(Builder $builder)
public function get(Builder $builder)
{
// First, we will retrieve the field's value using our composed behavior.
$value = $this->behavior->get(
Expand Down Expand Up @@ -95,7 +96,7 @@ public function runGet(Builder $builder)
* @param mixed $value
* @return void
*/
public function runUpdate(Builder $builder, $value)
public function update(Builder $builder, $value)
{
$this->behavior->update($builder->field, $value, $builder->id);
}
Expand All @@ -112,6 +113,18 @@ protected function runExpect($expected, $value)
return (gettype($value) === $expected) ? $value : null;
}

/**
* Check that the value matches the given pattern.
*
* @param string $pattern
* @param string $value
* @return mixed
*/
protected function runMatches($pattern, $value)
{
return preg_match($pattern, $value) ? $value : null;
}

/**
* Return the default value if the given value is empty or null.
*
Expand All @@ -133,7 +146,7 @@ protected function runDefault($default, $value)
/**
* Escape the value with the given function.
*
* @param string $func
* @param callable $func
* @param string $value
* @return string
*/
Expand Down
14 changes: 14 additions & 0 deletions tests/Unit/AcfTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,18 @@ public function fluentCall()

$this->assertEquals('Hello World', $value);
}

/** @test */
public function macrosApplyToBuilder()
{
Acf::macro('imageArray', function (Builder $builder) {
$builder
->expect('array')
->default(['url' => 'default-image.jpg']);
});
$builder = Acf::field('foo')->imageArray();

$this->assertEquals('array', $builder->expect);
$this->assertEquals(['url' => 'default-image.jpg'], $builder->default);
}
}
38 changes: 38 additions & 0 deletions tests/Unit/Fluent/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,44 @@ public function setRaw()
$this->assertTrue($builder->raw);
}

/** @test */
public function setMatch()
{
$builder = new Builder(new RunnerMock);

$return = $builder->matches('/(regex)/');

$this->assertSame($builder, $return);
$this->assertEquals('/(regex)/', $builder->matches);
}

/** @test */
public function applyMacro()
{
$builder = new Builder(new RunnerMock, [
'imageArray' => function (Builder $builder, $a, $b) {
$builder
->expect('array')
->default(['url' => 'default-image.jpg']);
},
]);
$return = $builder->imageArray('hi', 12);

$this->assertSame($builder, $return);
$this->assertEquals('array', $builder->expect);
$this->assertEquals(['url' => 'default-image.jpg'], $builder->default);
}

/**
* @test
* @expectedException \BadMethodCallException
*/
public function throwMethodNotFoundExceptionIfMacroDoesntExist()
{
$builder = new Builder(new RunnerMock);
$builder->nope();
}

/** @test */
public function builderGet()
{
Expand Down
Loading

0 comments on commit 55af756

Please sign in to comment.