Welcome to the upgrade guide for Factory Muffin. We've tried to cover all changes from 1.4 through to the current release. If we've missed anything, feel free to create an issue, or send a pull request. From 2.0, we are following the PSR-2 coding standard, and semantic versioning, so there will be no BC breaks until a major release other than essential fixes, and any changes will be documented here.
Factory Muffin 3.0 has many changes and improvements. Unfortunately, we were not able to provide complete documentation here, but would accept pull requests to improve our upgrading docs.
Our integration with faker has been totally re-written, and has been moved to a new repo, Factory Muffin Faker.
We are now deleting saved models in the reverse order they were saved to ensure relationships behave correctly.
The exceptions have been cleaned up, and reviewed since 2.0.
In your composer.json, add:
{
"require-dev": {
"league/factory-muffin": "3.0.*"
}
}
The Factory Muffin Facade
class is gone in 3.x. All of the static methods from the facade are now instance methods directly on the FactoryMuffin
class. You must create your own instance of FactoryMuffin
and store it where you please. A reasonable choice is to store it as a protected property of your base test case object.
For example, in 2.x, you might load your factories in a base TestCase
as such:
static function setupBeforeClass()
{
Facade::loadFactories(__DIR__ . '/factories');
}
In 3.x you must use an instance variable instead:
function setUp()
{
parent::setUp();
$this->fm = new FactoryMuffin();
$this->fm->loadFactories(__DIR__ . '/factories');
}
In all of your inheriting test cases, you can simply replace the static call to the facade with a call to the instance you created.
For example, in 2.x, you might have created a shiny object this way:
Facade::create('ShinyObject');
In 3.x, simply call create
from the instance instead:
$this->fm->create('ShinyObject');
The signature of the define
method has changed. Rather than passing an array of definitions as the second parameter, you must instead create the object first and then set the definitions.
For example, in 2.x:
FactoryMuffin::define('Agent', [
'name' => 'Bill Smith'
]);
Now becomes:
$fm->define('Agent')->setDefinitions([
'name' => 'Bill Smith'
]);
Be aware that the $fm
variable is available in your factory definition files and refers to the same instance of FactoryMuffin
from which you called the loadFactories
method. Really it is just an alias of $this
from inside the instance.
Now you can define multiple different factory definitions for your models. You can do this by prefixing the model class name with your "group" followed by a colon. This results in you defining your model like this: League\FactoryMuffin\Facade::define('myGroup:Fully\Qualifed\ModelName', array('foo' => 'bar'))
. You don't have to entirely define your model here because we will first look for a definition without the group prefix, then apply your group definition on top of that definition, overriding attribute definitions where required.
Before you try to create your model instance, we'll check whether the class actually exists, thus avoiding a fatal error. If the class does not exist, we'll throw a League\FactoryMuffin\Exceptions\ModelNotFoundException
. Note that this exception is new in 2.1.
We'll now keep track of objects waiting to be saved separately from objects that have already been saved. This means the saved
and isSaved
functions will behave slightly differently. Details of the changes are listed below:
- The
saved
function will now return an array of objects actually saved to the database whereas before it would return an array of objects that were either saved, or were going to be saved later. - The
isSaved
function will now check if your object is actually saved to the database whereas before it would check if it was saved, or was going to be saved later. - The
pending
function is new, and returns an array of objects that will be saved to the database later, but have not been saved yet. - The
isPending
function is new, and checks if your object is going to be saved to the database later, but has not been saved yet. - The
isPendingOrSaved
function is new, and checks if your object is either saved in the database, or will be saved to the database later. This behaves as the oldisSaved
function used to behave.
When you define your definitions, you may optionally specify a callback to be executed on model creation/instantiation as a third parameter. We will pass your model instance as the first parameter to the closure if you specify one. We additionally pass a boolean as the second parameter that will be true
if the model is being persisted to the database (the create function was used), and false
if it's not being persisted (the instance function was used). Note that if you specify a callback and use the create function, we will try to save your model to the database both before and after we execute the callback.
Now, we additionally pass a boolean as the second parameter that will be true
if the model is being persisted to the database (the create function was used), and false
if it's not being persisted (the instance function was used). This is, of course, in addition to passing the object instance as the first parameter. Previously, you'd have had to call the isSaved
function on the facade, but this is no longer needed now for checking if the object is being persisted. Note that due to changes to the isSaved
function, we're actually calling isPendingOrSaved
under the hood now.
You may now call League\FactoryMuffin\Facade::setCustomMaker(function ($class) { return new $class('example'); })
in order to register a closure to customise the model creation. This will be used internally by Factory Muffin rather than us just straight up using new $class()
.
You may now call League\FactoryMuffin\Facade::setCustomSetter(function ($object, $name, $value) { $object->set($name, $value); })
in order to register a closure to customise the attribute setting. This will be used internally by Factory Muffin when setting your attributes rather than us just using $object->$name = $value
.
You may now call League\FactoryMuffin\Facade::setCustomSaver(function ($object) { $object->save(); $object->push(); return true; })
in order to save your object in a custom way. This will be used internally by Factory Muffin when saving your object rather than us just using $object->save()
.
You may now call League\FactoryMuffin\Facade::setCustomDeleter(function ($object) { $object->forceDelete(); return true; })
in order to delete your object in a custom way. This will be used internally by Factory Muffin when deleting your object rather than us just using $object->delete()
.
We hope this change allows you to use even more completely custom models with Factory Muffin.
We've added a reset
method to the facade that will re-create the underlying factory instance.
We've added a getFaker
method to the factory (also available through the facade), that will return the underlying faker instance. This method actually already existed in 2.0, but was previously private
rather than public
.
We no longer use include_once
to load your definitions with the loadFactories
method. We use include
instead so that your definitions can be correctly re-added to your factory instance if you use the reset
method.
There is a very tiny change to the exception message of the League\FactoryMuffin\Exceptions\NoDefinedFactoryException
. If you were relying on that for some reason, watch out for that.
In your composer.json, add:
{
"require-dev": {
"league/factory-muffin": "2.1.*"
}
}
Version 2.0 marks a major file milestone in this project, under the new name of "Factory Muffin". We see a large number of improvements and some breaking changes. Within this section of the upgrading guide, you will see "the xyz
function can be called". You should assume that these functions should be called statically on the League\FactoryMuffin\Facade
class.
Every class has moved, so here's a summary of the changes:
- The root namespace has been moved from
Zizaco\FactoryMuff
toLeague\FactoryMuffin
. You should now access the facade usingLeague\FactoryMuffin\Facade::fooBar()
. - Many generator (kind) classes have been removed in favour of the faker alternatives. Those remaining can be found under the
League\FactoryMuffin\Generators
namespace. - There are many more exceptions, and the names of the existing exceptions have changed. The exceptions can be found under the
League\FactoryMuffin\Exceptions
namespace.
A detailed list of every change with FQCNs is listed below:
- Moved:
Zizaco\FactoryMuff\FactoryMuff
=>League\FactoryMuffin\Factory
- Moved:
Zizaco\FactoryMuff\Facade\FactoryMuff
=>League\FactoryMuffin\Facade
- Moved:
Zizaco\FactoryMuff\SaveException
=>League\FactoryMuffin\Exceptions\SaveFailedException
- Moved:
Zizaco\FactoryMuff\NoDefinedFactoryException
=>League\FactoryMuffin\Exceptions\NoDefinedFactoryException
- Moved:
Zizaco\FactoryMuff\Kind
=>League\FactoryMuffin\Generators\Base
- Moved:
Zizaco\FactoryMuff\Kind\Call
=>League\FactoryMuffin\Generators\Call
- Moved:
Zizaco\FactoryMuff\Kind\Closure
=>League\FactoryMuffin\Generators\Closure
- Moved:
Zizaco\FactoryMuff\Kind\Factory
=>League\FactoryMuffin\Generators\Factory
- Moved:
Zizaco\FactoryMuff\Kind\Generic
=>League\FactoryMuffin\Generators\Generic
- Added:
League\FactoryMuffin\Exceptions\DeleteFailedException
- Added:
League\FactoryMuffin\Exceptions\DeleteMethodNotFoundException
- Added:
League\FactoryMuffin\Exceptions\DeletingFailedException
- Added:
League\FactoryMuffin\Exceptions\DirectoryNotFoundException
- Added:
League\FactoryMuffin\Exceptions\MethodNotFoundException
- Added:
League\FactoryMuffin\Exceptions\ModelException
- Added:
League\FactoryMuffin\Exceptions\SaveMethodNotFoundException
- Removed:
Zizaco\FactoryMuff\Kind\Date
- Removed:
Zizaco\FactoryMuff\Kind\Integer
- Removed:
Zizaco\FactoryMuff\Kind\Name
- Removed:
Zizaco\FactoryMuff\Kind\String
- Removed:
Zizaco\FactoryMuff\Kind\Text
It also should be noted that we've moved from PSR-0 to PSR-4 for autoloading.
Under it's new name, the facade class now uses __callStatic
to dynamically call the underlying factory instance. Also, note that all public methods that would have returned void, return the factory instance in order to support method chaining.
Having a public static factory property is no longer supported. You must use the define
function introduced in the 1.5.x series. You may call it like this: League\FactoryMuffin\Facade::define('Fully\Qualifed\ModelName', array('foo' => 'bar'))
. We have provided a nifty way for you to do this in your tests. PHPUnit provides a setupBeforeClass
function. Within that function you can call League\FactoryMuffin\Facade::loadFactories(__DIR__ . '/factories');
, and it will include all files in the factories folder. Within those php files, you can put your definitions (all your code that calls the define function). The loadFactories
function will throw a League\FactoryMuffin\Exceptions\DirectoryNotFoundException
exception if the directory you're loading is not found. A full example is included in the readme.
We now refer to what were previously the kind classes, as generator classes. We've removed some of these in favour of the faker alternatives. We currently provide the following generators: Call
, Closure
, Factory
, and Generic
. The call, closure, and factory generators have not changed significantly since previous versions, and the generic generator still provides access to the faker generators. The closure generator will now pass the instance of your model into your closure as the first parameter too.
There are two syntax changes to watch out for:
- You can now use a
;
to send multiple arguments to the generators. - Unique and optional attributes are now supported by prefixing the definition with
unique:
oroptional:
.
The removed generators are Date
, Integer
, Name
, String
, and Text
, however, these are still callable as they are available through the generic generator using faker. Here are some possible changes you will need to make:
- Instead of using
integer|8
, you can userandomNumber|8
. - Instead of using
string
, you can usesentence
, orword
. - Instead of using
name
, you can use things likefirstNameMale
. date
andtext
can be used in the same way you were using them before.
It should be noted that we are using faker 1.4 which is a change since the previous release. We've made a more strict version requirement to avoid potential BC breaks caused by faker.
The create
function can be called in the same way, but has internal improvements. Now, it will also save anything you generate with the Factory
generator too. We now have a new function called seed
, which accepts an additional argument at the start which is the number of models to generate in the process. The seed
function will effectively be calling the create
function over and over. It should be noted that you can set a custom save function before you get going with the setSaveMethod
function. Also, a reminder that the instance
function is still available if you don't want database persistence.
You may encounter the following exceptions:
League\FactoryMuffin\Exceptions\NoDefinedFactoryException
will be thrown if you try to create a model and you haven't defined a factory definition for it earlier.League\FactoryMuffin\Exceptions\SaveFailedException
will be thrown if the save function on your model returns false.League\FactoryMuffin\Exceptions\SaveMethodNotFoundException
will be thrown if the save function on your model does not exist.- Any other exception thrown by your model while trying to create or save it.
There are 2 other helper functions available. You may call saved
to return an array of all the saved objects. You may call isSaved
with an instance of a model to check if it's saved.
You can now delete all your saved models with the deleteSaved
function. It should be noted that you can set a custom delete function before you get going with the setDeleteMethod
function.
If one or more models cannot be deleted, a League\FactoryMuffin\Exceptions\DeletingFailedException
will be raised after we have attempted to delete all the saved models. You may access each underlying exception, in the order they were thrown during the whole process, with the getExceptions
function which will return an array of exceptions. You may encounter the following exceptions:
League\FactoryMuffin\Exceptions\DeleteFailedException
will be thrown if the delete function on your model returns false.League\FactoryMuffin\Exceptions\DeleteMethodNotFoundException
will be thrown if the delete function on your model does not exist.- Any other exception thrown by your model while trying to delete it.
It's recommended that you call the deleteSaved
function from PHPUnit's tearDownAfterClass
function. A full example is included in the readme.
The exceptions have been completely overhauled. Each exception is documented with the documentation for the functions that throw them.
You can see a diagram showing the exception hierarchy here:
The attributesFor
function no longer accepts a class name as the first argument, and the generateAttr
function no longer accepts a class name as a second argument. Please pass an actual model instance to both functions instead.
We now require php 5.3.3 as a minimum version. This is an increase on our previous requirement of php 5.3.0.
In your composer.json, add:
{
"require-dev": {
"league/factory-muffin": "2.0.*"
}
}
We now use the faker package, so our Zizaco\FactoryMuff\Wordlist
class has been removed. All your previous definitions should still work in as close to the same way as possible, but watch out for any minor differences. With the addition of the faker library, far more definitions are now possible since any definitions not natively provided by us, fall back to the faker package. Also, it should be noted you may use closures now to generate completely custom attributes. The new classes can be found under the Zizaco\FactoryMuff\Kind
namespace.
In your composer.json, add:
{
"require-dev": {
"league/factory-muffin": "1.6.*"
}
}
We've added some exceptions for certain events:
- The
Zizaco\FactoryMuff\SaveException
will now be thrown on model creation if the save function returns false. - The
Zizaco\FactoryMuff\NoDefinedFactoryException
will now be thrown if you attempt to generate attributes for an model that has no defined factory. Previously, php would raise a fatal error.
Instead of having a public static $factory = array('foo' => 'bar')
property on your model, you should call Zizaco\FactoryMuff\Facade\FactoryMuff::define('Fully\Qualifed\ModelName', array('foo' => 'bar'))
to define your model's factory. Note that the property on the model is still supported for now, but is deprecated, and will be removed in 2.0.
In your composer.json, add:
{
"require-dev": {
"league/factory-muffin": "1.5.*"
}
}