Skip to content

Latest commit

 

History

History
297 lines (192 loc) · 18.2 KB

UPGRADING.md

File metadata and controls

297 lines (192 loc) · 18.2 KB

Upgrading

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.

Upgrading from 2.1.x to 3.0.x

Introduction

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.

Faker Support Changed

Our integration with faker has been totally re-written, and has been moved to a new repo, Factory Muffin Faker.

Deleting Saved Models

We are now deleting saved models in the reverse order they were saved to ensure relationships behave correctly.

Exceptions

The exceptions have been cleaned up, and reviewed since 2.0.

Installing This Version

In your composer.json, add:

{
    "require-dev": {
        "league/factory-muffin": "3.0.*"
    }
}

No More Facade, No More Statics

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');

Factory Definitions

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.

Upgrading from 2.0.x to 2.1.x

Multiple Factory Definitions

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.

Small Change To Model Creation

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.

Changes To The Saving Process

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 old isSaved function used to behave.

Creation/Instantiation Callbacks

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.

Addition To The Closure Generator

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.

Additional Customisation

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.

Other Minor Changes

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.

Installing This Version

In your composer.json, add:

{
    "require-dev": {
        "league/factory-muffin": "2.1.*"
    }
}

Upgrading from 1.6.x to 2.0.x

Introduction

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.

Class Name Changes

Every class has moved, so here's a summary of the changes:

  • The root namespace has been moved from Zizaco\FactoryMuff to League\FactoryMuffin. You should now access the facade using League\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.

Facade Changes

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.

Factory Definitions

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.

Generator (Kind) Changes

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: or optional:.

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 use randomNumber|8.
  • Instead of using string, you can use sentence, or word.
  • Instead of using name, you can use things like firstNameMale.
  • date and text 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.

Creating And Seeding

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.

Deleting

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.

Exceptions

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:

diagram

Other BC Breaks

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.

Installing This Version

In your composer.json, add:

{
    "require-dev": {
        "league/factory-muffin": "2.0.*"
    }
}

Upgrading from 1.5.x to 1.6.x

Faker Usage

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.

Installing This Version

In your composer.json, add:

{
    "require-dev": {
        "league/factory-muffin": "1.6.*"
    }
}

Upgrading from 1.4.x to 1.5.x

Exceptions

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.

Factory Definitions

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.

Installing This Version

In your composer.json, add:

{
    "require-dev": {
        "league/factory-muffin": "1.5.*"
    }
}