Skip to content

Instantly share code, notes, and snippets.

@palypster
Last active March 14, 2020 16:09
Show Gist options
  • Save palypster/100e4e0babcc4a4250811a7bdd27b7ca to your computer and use it in GitHub Desktop.
Save palypster/100e4e0babcc4a4250811a7bdd27b7ca to your computer and use it in GitHub Desktop.
<?php
namespace App;
use Illuminate\Database\Eloquent\Factory;
class ExtendedFactory extends Factory {
/**
* The registered after creating callbacks.
*
* @var array
*/
protected $beforeCreating = [];
/**
* Define a callback to run before creating a model.
*
* @param string $class
* @param callable $callback
* @param string $name
* @return $this
*/
public function beforeCreating($class, callable $callback, $name = 'default')
{
$this->beforeCreating[$class][$name][] = $callback;
return $this;
}
/**
* Define a callback to run before creating a model with given state.
*
* @param string $class
* @param string $state
* @param callable $callback
* @return $this
*/
public function beforeCreatingState($class, $state, callable $callback)
{
return $this->beforeCreating($class, $callback, $state);
}
/**
* Create a builder for the given model.
*
* @param string $class
* @return \Illuminate\Database\Eloquent\FactoryBuilder
*/
public function of($class)
{
return new ExtendedFactoryBuilder(
$class, $this->definitions, $this->states,
$this->afterMaking, $this->afterCreating, $this->beforeCreating, $this->faker
);
}
public function cascade($class, callable $callback)
{
return $this->cascadeState($class, 'default', $callback);
}
public function reuse($class, callable $callback)
{
return $this->reuseState($class, 'default', $callback);
}
public function cascadeState($class, $state, callable $callback)
{
return $this->beforeCreatingState($class, 'cascade-' . $state, $callback);
}
public function reuseState($class, $state, callable $callback)
{
return $this->afterMakingState($class, 'reuse-' . $state, $callback);
}
}
<?php
namespace App;
use Faker\Generator as Faker;
use Illuminate\Database\Eloquent\FactoryBuilder;
use Illuminate\Database\Eloquent\Model;
class ExtendedFactoryBuilder extends FactoryBuilder
{
/**
* @var array
*/
private $beforeCreating = [];
public function __construct($class, array $definitions, array $states,
array $afterMaking, array $afterCreating, array $beforeCreating, Faker $faker)
{
parent::__construct($class, $definitions, $states, $afterMaking, $afterCreating, $faker);
$this->beforeCreating = $beforeCreating;
}
/**
* Create a collection of models and persist them to the database.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|mixed
*/
public function create(array $attributes = [])
{
$results = $this->make($attributes);
if ($results instanceof Model) {
$this->callBeforeCreating(collect([$results]));
$this->store(collect([$results]));
$this->callAfterCreating(collect([$results]));
} else {
$this->callBeforeCreating(collect([$results]));
$this->store($results);
$this->callAfterCreating($results);
}
return $results;
}
/**
* Run before creating callbacks on a collection of models.
*
* @param \Illuminate\Support\Collection $models
* @return void
*/
public function callBeforeCreating($models)
{
$this->callBefore($this->beforeCreating, $models);
}
/**
* Call before callbacks for each model and state.
*
* @param array $beforeCallbacks
* @param \Illuminate\Support\Collection $models
* @return void
*/
protected function callBefore(array $beforeCallbacks, $models)
{
$states = array_merge(['default'], $this->activeStates);
$models->each(function ($model) use ($states, $beforeCallbacks) {
foreach ($states as $state) {
$this->callBeforeCallbacks($beforeCallbacks, $model, $state);
}
});
}
/**
* Call before callbacks for each model and state.
*
* @param array $beforeCallbacks
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $state
* @return void
*/
protected function callBeforeCallbacks(array $beforeCallbacks, $model, $state)
{
if (! isset($beforeCallbacks[$this->class][$state])) {
return;
}
foreach ($beforeCallbacks[$this->class][$state] as $callback) {
$callback($model, $this->faker);
}
}
/**
* Determine if the given state has an "after" of "before" callback.
*
* @param string $state
* @return bool
*/
protected function stateHasAfterCallback($state)
{
return isset($this->afterMaking[$this->class][$state]) ||
isset($this->beforeCreating[$this->class][$state]) ||
isset($this->afterCreating[$this->class][$state]);
}
public function cascade(array $states = [])
{
if (empty($states)) {
$states = ['default'];
}
$this->states(array_map(function($state){
return 'cascade-' . $state;
}, $states));
return $this;
}
public function reuse(array $states = [])
{
if (empty($states)) {
$states = ['default'];
}
$this->states(array_map(function($state){
return 'reuse-' . $state;
}, $states));
return $this;
}
/**
* Set the states to be applied to the model.
*
* @param array|mixed $states
* @return $this
*/
public function states($states)
{
$this->activeStates = array_merge($this->activeStates, is_array($states) ? $states : func_get_args());
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment