Codeception DataFactory interaction between helper and other factories - php

I'm looking into using DataFactory's in Codeception for seeding of data, and for use in our acceptance tests. In the documentation there's mention of 2 approaches, one using the helper file and one using factories files.
We load both options using this snippet from our acceptance.suite.yml
class_name: AcceptanceTester
modules:
enabled:
- Db
- WebDriver
- \Helper\Acceptance
- Doctrine2:
connection_callback: getEntityManager
- DataFactory:
factories: tests/_support/factories
depends: Doctrine2
- \Helper\Factory
Both of the options seem to load correctly. As per the documentation I can then define factories like this, which will allow interaction with Doctrine.
// tests/_support/Helper/Factory.php
class Factory extends Module
{
/**
* #param array $settings
* #throws \League\FactoryMuffin\Exceptions\DefinitionAlreadyDefinedException
* #throws \Codeception\Exception\ModuleException
*/
public function _beforeSuite($settings = [])
{
/** #var Module\DataFactory $factory */
$factory = $this->getModule('DataFactory');
/** #var EntityManager $em */
$em = $this->getModule('Doctrine2')->_getEntityManager();
$factory->_define(User::class,[
// generate random user name
'name' => Faker::name(),
]);
parent::_beforeSuite($settings);
}
}
As per the other option, I can also create factories by loading all files from within tests/_support/factories, such as below:
// tests/_support/factories/seed.php
use League\FactoryMuffin\Faker\Faker;
/** #var \League\FactoryMuffin\FactoryMuffin $fm */
$user = $fm->create(User::class);
dd($user);
However, the seed.php version cannot seem to share the Factory, and errors with:
The model definition 'User' is undefined.
I wondered if maybe this could be solved by moving the Factory.php logic into the initialize() method but this seems to be called before FactoryMuffin has been initiliazed.
The documentation for this with codeception seems a bit sparse, and the FactoryMuffin docs, while better, don't cover Codeception integration. Just trying to work out if i'm missing something, or I just need to repeat the code in each place if I want to use both files/methods.

This is an old question and technology moves fast so the documentation has likely changed since this was originally asked but I'll make an attempt in case anyone else stumbles across it like I did.
You're using the DataFactory module which is great as it comes with the integration for Codeception out of the box. The two methods you've described are actually ways of integrating DataFactory with your data. By creating factory files, you've given DataFactory a means of generating data. But what if you have some data already in the database that you'd like to use in your tests as well? That's where you would use the Helper class. According to the DataFactory Module docs:
In cases you want to use data from database inside your factory definitions you can define them in Helper. For instance, if you use Doctrine, this allows you to access EntityManager inside a definition.
As for your issue of seed.php not finding the User model, you need to specify it according to the definition given in your factory. For example, if your factory file looks similar to this
<?php
use League\FactoryMuffin\Faker\Facade as Faker;
$fm->define('app\models\User')->setDefinitions([
'name' => Faker::name(),
... // the rest of your properties here
]);
Then seed.php would look like
// tests/_support/factories/seed.php
use League\FactoryMuffin\Faker\Faker;
$user = $fm->create('app\models\User');
Once you have the DataFactory module installed and configured, you can simply call it within the appropriate testing suite via have, haveMultiple, or make. See the Codeception Docs

Related

Override Singleton in Laravel Container

I'm wondering if there's a simple way to override a singleton service set in the core of the Laravel framework?
e.g. I'm trying to rewrite the app:name command service '' with the following provider:
use Hexavel\Console\AppNameCommand;
use Illuminate\Console\Events\ArtisanStarting;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ServiceProvider;
class NameCommandProvider extends ServiceProvider
{
/**
* Register any other events for your application.
*
* #param \Illuminate\Contracts\Events\Dispatcher $events
* #return void
*/
public function boot(Dispatcher $events)
{
$events->listen(ArtisanStarting::class, function ($event) {
$event->artisan->resolve('command.app.name');
}, -1);
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->singleton('command.app.name', function ($app) {
return new AppNameCommand($app['composer'], $app['files']);
});
}
}
I'm 100% everything is working due to extensive checks put no matter what order I put my service provider (above or below ConsoleSupportServiceProvider) it still loads the original AppNameCommand over my custom one.
I've already got a work around BUT it would be nice to know about the behaviour of singleton services for the future if this is at all possible? (This is using Laravel 5.2 if that makes any difference.)
There's actually a cleaner way to do this. You basically want to extend a core binding, which can be achieved by using the extend method:
$this->app->extend('command.app.name', function ($command, $app) {
return new AppNameCommand($app['composer'], $app['files']);
});
Jason Lewis has a really nice article regarding Laravel's IoC on Tutsplus. Make sure to check it out ;)
I looked at this case and it seems it not the easy one. If you use singleton in your custom Provider it will be finally overridden by default provider (deferred one) so it seems it won't be the way.
After checking that simple approach doesn't work, what you need to do in such case is analysing what is happening when Laravel registers this command.
So in your case you search first for command.app.name - you see it's in Illuminate\Foundation\Providers\ArtisanServiceProvider and there is method registerAppNameCommand you would like to probably override.
So now you look for occurences of ArtisanServiceProvider to see where it's launched - you see it's in Illuminate\Foundation\Providers\ConsoleSupportServiceProvider in $providers property (which you would like probably to change).
So finally you should look for occurrences of ConsoleSupportServiceProvider and you see it's in config/app.php.
So what you need to do in this case:
Change in config/app.php - change Illuminate\Foundation\Providers\ConsoleSupportServiceProvider into your custom one ConsoleSupportServiceProvider
In your custom one you should extend from \Illuminate\Foundation\Providers\ConsoleSupportServiceProvider but change in $providers from Illuminate\Foundation\Providers\ArtisanServiceProvider into your custom ArtisanServiceProvider
finally create custom ArtisanServiceProvider which will extend from \Illuminate\Foundation\Providers\ArtisanServiceProvider where you override registerAppNameCommand using custom class in singleton
Using this way you will achieve your goal (I've verified it that custom class will be used running command php artisan app:name).
Alternatively you might want in your custom ArtisanServiceProvider remove 'AppName' => 'command.app.name', from $devCommands and use your custom service provider as you showed where you register your singleton but I haven't tried this approach.

How do I add a 'validator' class to this code to get it to work?

This is the error that I keep receiving: Reflection Exception Class validator does not exist This is the code causing the problems:
use Illuminate\Support\ServiceProvider;
class DeskServiceProvider extends ServiceProvider
{
/**
* Register bindings
*
* #return void
*/
public function register()
{
$this->repositories();
$this->app->bind('Desk\Forms\MessageForm', function($app) {
$validator = $app->make('validator')->make([], []);
return new \Desk\Forms\MessageForm($validator);
});
}
}
I now know that I need to add a Validator class but I am not sure where or what to put in it. Thank you for all your help.
Your question is a little confusing, as is your code. If looks like you're trying to bind a service.
$this->app->bind('Desk\Forms\MessageForm'
However, instead of telling Laravel the service name you want to use to identify your service (like db, or message_form, etc.) you're passing it a class name (Desk\Forms\MessageForm).
Then, you're using the application's make factory to instantiate a validator object. It's not clear if you're trying to use make to instantiate an object from a class named Validator, or if you're trying to instantiate a service object from a service named validator. If the later, it doesn't look like a validator service exists in your application. If the former, it doesn't look like a class named Validator is defined anywhere Laravel can autoload from.
Regarding the next obvious question: Where can Laravel autoload from, you either want this Validator class in your composer package's src folder, named in a way that's PSR valid. If you're not using composer and this is a local application, the easiest thing to do is drop the file in
app/models/Validator.php
However, it's also not clear from your question if you're trying to use the Laravel built-in Validator service facade/object. A better question might yield a better answer. (possibly of interest, and a self link, I'm in the middle of writing a series of articles that explains the Laravel application container, which you may find useful.).

Understanding of a line of code in Laravel routing

after learning procedural I'm trying to learning OOP in PHP, and after studying some theory I'm trying to apply it studing the use of the Laravel framework.
I've found on my book this part of code, for routing, but I can't really understand it in OOP.
If I'm not wrong, the first part seems to me a static method of a 'Route' class, but then I find the second part
'->where('id','[0-9]+');' that seems dynamic and relative to an instance and is confusing me.
Can someone please help me understanding?
Route::get('cats/{id}', function($id){
return "Cat #$id";
})->where('id', '[0-9]+');
If I'm not wrong, the first part seems to me a static method of a 'Route' class, Sorry but you are wrong here. Actually Laravel provides Facade class for each component and here Route is a Facade of underlying Router class. This is how that Facade class looks like:
<?php namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Routing\Router
*/
class Route extends Facade {
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'router'; }
}
You may noticed that, it contains only one method and it returns the original/underlying class name that contains the method, actually it's the key name using which the class is added into the IoC container. So, Laravel behind the scene, makes an instance of that Illuminate/Routing/Router.php class from the IoC container and calls the method, it's may looks unclear to you but it's a little tricky and it's not possible to answer in more details here but you may visit Laravel facade and get a better explanation of it.
So, finally, Laravel calls get() method from the Router.php class and it returns an instance of Route class/object and the where method of Route class then get called using method chaining (PHP-5 feature), that's all. Read the source code of classes, you'll get a better idea.
Check the Illuminate\Support\Facades folder, you can find so many facade classes which are actually a wrapper over it's original class/component. Also check out the IoC container in Laravel's documentation, it's necessary to get a clear idea of it to work with Laravel framework.

Laravel 4 DB:raw() is unavailable when running outside of framework

I'm using the [illuminate/database component][1] from Laravel 4 through composer - and while it generally works well, the DB facade seems to be broken in this standalone version. This meant I was unable to use static functions such as DB::raw(). It seems like the DB facade is even included in the package, but it doesn't work with ::raw().
I'm trying to do something like this -
...->orderBy(DB::raw('RAND()'))
Capsule::raw() is available, and is linked to the default connection's raw().
Also, what I did is I created a class:
/**
* #method static raw($value)
* #method static array select($query, $bindings = [], $useReadPdo = true)
* ...etc.
*/
class DB extends Manager
{
}
so that
You can use DB::raw().
IDE code completion works.
I found a partial solution, but if anyone has any ideas that work better, I'm eager to hear them (it seems the original Capsule package actually had support built in, maybe it was lost when it was merged, or maybe I'm using it incorrectly?)
use Illuminate\Database\Capsule\Manager as Capsule;
$connection = Capsule::connection();
// You can now use $connection->raw() in place of DB::raw()
...->orderBy($connection->raw('RAND()'))

Symfony2 Form Generation from Vendor Classes

Working with Symfony2 and trying to figure out how best to incorporate Vendor libraries. Calling vendor library methods is very easy using the routing configs: use the appropriate namespace, generate the class name, the method to call, and the arguments from a couple path components and query vars, voila, instant integration.
I'm having trouble with forms, though. My goal seems like it should be very easy. I want to make forms in Symfony2 from vendor classes. As a specific example, consider the google-api-php-client. It seems like ->createForm() would work best, because it bridges to the ORM and validation so nicely. However, it relies on a a MyBundle\Form\ThingType file and class. To create this class, I need an entity within my bundle. I can't (or haven't been able to figure out how to) just use existing "Entities" from vendor libraries. Creating the "Entity" in the Symfony nomenclature when a "Model" already exists in the API lingo seems to be inflexible and very un-D.R.Y.
The other method I've gotten to work is using
$formBuilder = $this->createFormBuilder(new GoogleApi\Contrib\Event);
then
foreach(get_object_vars($event) as $prop) { $formBuilder->add($prop); }
but this does not utilize the seemingly ready-made bridge between the API documentation and the built-in validation tools, and it also means each individual data type is going to have to be declared individual or array to decide whether to include a collection of class-based forms or a single, class-based form.
In short, I want to use the properties and dataType information available in the API, and, if necessary, the Resource representations like this one to create a simple function (like my call function) for creating nested, self-validating forms for the classes in the Google API. I want to accomplish this without creating a bunch of "Entities" and "FormTypes" that simply rewrite what is already written in the library.
Did the vendor library not have any installation details? You should generally calls vendor things from the controller using service calls like $this->get('vendor.name.form_object') rather than calling the class at the service will include any needed dependencies.
Also the entity that you would create in your bundle would only be the basic entity that would then extend their premade abstract classes. For example (taken from https://github.com/FriendsOfSymfony/FOSUserBundle)
<?php
// src/Acme/UserBundle/Entity/User.php
namespace Acme\UserBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
}
If you want to add any of your own entity items then you can do it in this, but still keep the methods and properties from the base entity/model.

Categories