How to handle doctrine2 data fixtures (flat file) - php

I am looking at doctrine2 and how to handle data fixtures. I am especially interested in reading them from flat files (csv, yaml, xls).
In doctrine 1.2 data fixtures are handled like this: http://www.doctrine-project.org/projects/orm/1.2/docs/manual/data-fixtures/en#data-fixtures
Any suggestion how to handle this in doctrine2?

As already mentioned by Steven the fixture-feature comes as a separate repo.
It took me some time to figure out how to install the data fixtures feature in Symfony2, so here is how I did it:
add sources to your deps file:
[doctrine-fixtures]
git=http://github.com/doctrine/data-fixtures.git
[DoctrineFixturesBundle]
git=http://github.com/symfony/DoctrineFixturesBundle.git
target=/bundles/Symfony/Bundle/DoctrineFixturesBundle
update your vendors
$ php bin/vendors install
register in in autoload.php:
$loader->registerNamespaces(array(
//...
'Doctrine\\Common\\DataFixtures' => __DIR__.'/../vendor/doctrine-fixtures/lib',
'Doctrine\\Common' => __DIR__.'/../vendor/doctrine-common/lib',
//..
));
add class which subclasses FixtureInterface:
<?php
use Doctrine\ORM\EntityManager,
Doctrine\Common\DataFixtures\FixtureInterface;
/**
*
* setup of initial data for the unit- and functional tests
* #author stephan
*/
class LoadTestingData implements FixtureInterface{
/**
*
* #param EntityManager $manager
*/
public function load($manager) {
$user = new User();
$user->setUsername("testuser");
$manager->persist($user);
}
//...
load data fixtures via console command
./app/console doctrine:data:load

There is a git submodule for that in the official doctrine git repo https://github.com/doctrine/data-fixtures
I am currently using it and it works pretty well.

I use class-based fixtures, much better this way because you can handle associations and dependencies easily with the EntityManager directly, also easy for using in unit tests.
Here is the library I use with Zend Framework modules, but you can just write your own loader. There is a command line script too.

Related

Codeception DataFactory interaction between helper and other factories

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

Doctrine standalone without cli-config.php

I want to integrate Doctrine ORM into my (non-Symfony) project. I already done this in another project and used the famous cli-config.php into the project root directory.
But now, in my new project, I use the Symfony Console component and the Dependency Injection component (to reference services and commands, by tagging them).
1. I absolutely don't want to have a cli-config.php in the project root. How the Sf Doctrine Bundle do this?
2. Also (but it is less important), I would like to have the Doctrine commands into my project CLI.
What would be the best way to do this? Create references to Doctrine commands into my services.yml ? Or create local "decorator commands" that call Doctrine commands via PHP?
Finally, after some googling and experiments, I found a complete solution.
Just read the doctrine.php in vendor/bin. It is very easy to avoid the hardcoded config-cli.php file.
1. Create an entity manager
In my case, I use a factory and this method hydrates the doctrine.em service.
($config is specific to my app, change values to use your own logic.)
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
public function createEntityManager()
{
$config = $this->get('config');
$metadataConfig = Setup::createAnnotationMetadataConfiguration(
[$config->meta('app_path') . '/Entity'],
$config->oc->doctrine->dev_mode
);
return EntityManager::create((array) $config->oc->doctrine->connection, $metadataConfig);
}
2. Merge Doctrine CLI commands in your CLI commands
Somewere in your code, like in some bootstrap.php, you probably declare your Symfony\Component\Console\Application command line interface, that's how I do this (the foreach simply adds commands defined in my services.yml file):
$application = new Application('MyApp CLI', '0.0.1');
$services = $container->findTaggedServiceIds('oc.command');
foreach(array_keys($services) as $serviceId)
{
$application->add($container->get($serviceId));
}
$application->run();
Now, we simply ask Doctrine to inject its commands into our Application:
$application = new Application('MyApp CLI', '0.0.1');
$helperSet = ConsoleRunner::createHelperSet($container->get('doctrine.em'));
$application->setHelperSet($helperSet);
ConsoleRunner::addCommands($application);
$services = $container->findTaggedServiceIds('oc.command');
foreach(array_keys($services) as $serviceId)
{
$application->add($container->get($serviceId));
}
$application->run();
That's it! You can also only add a subset of the Doctrine commands by using arsfeld's answer on this GitHub issue.
3. Bonus: only import needed commands and rename them
You can create decorator commands that inherit Doctrine commands (this is useful to redefine the name of Doctrine commands, as Symfony Doctrine Bundle does, eg. orm:validate-schema -> doctrine:schema:validate).
To do this, remove the line ConsoleRunner::addCommands($application); we added in step 2. For each command you want to redefine, you will need to create an register a new command in your app. This command will "extends" the target Doctrine command and will override the configure() method.
Here is an example with orm:validate-schema:
<?php
namespace MyApp\Command\Doctrine;
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand;
class SchemaValidateCommand extends ValidateSchemaCommand
{
protected function configure()
{
parent::configure();
$this->setName('doctrine:schema:validate');
}
}
Some Doctrine commands have aliases that will pollute your command namespaces, like orm:generate-entities and orm:generate:entities.
To remove these aliases, in configure(), add ->setAliases(array()).
$this->setName('doctrine:generate:entities')->setAliases([]);
Congratulations, you just redone the Symfony Doctrine Bundle :p (jk)

Symfony :HOW TO Create Shared -General- (Helper) used in multiple bundles

I had many helper function -Grouped In Classes - for ( formatting Strings And Dates , URL Helpers ) that i want to use and share in several bundles , i need to know best practice about where i can put those helpers functions to be shared between bundles.
What came to my mind is to create a helper bundle and use this bundle in the other bundle that i have in my project or use the vendor helper.
So how i can do this and what is the best practice for Creating Shared Helper to be used in multiple bundles.
Please if there is any references i can look at please share it with me.
Thank you in advance.
Best practice would be to create a PHP library containing those classes. If you really need Symfony integration (eg. DIC configuration), then create bundle that depends on this library.
Every bundle that uses your bundle must list it among it's dependences in composer.json. So it will be installed autocratically every time you install bundle that depends on it.
There are plenty of great examples of libraries out there, that can be imported using composer and used even if they aren't bundles per se, take a look at Doctrine\Common for example.
Regardless, you can also create the bundle as you would any other bundle in Symfony, and structure the code as you see fit. You will notice with many of Doctrine's bundles will make use of the shared library Doctrine\Common.
If You have universal classes it should be grouped in one bundle ("Helper bundle" as You said) and if it is possible in Your case classes should be defined as services.
If You are using this bundle in more than one project and You want to upgrade that in the future, You should think about moving this bundle to separate repo and define it as a "standalone" bundle (so You can include that in your projects by composer and vendors directory.
I think Best practice is create helper Bundle and create service in helper bundle
Then you can use service in several bundle.
dummy example: in your service Helper.php
namespace HelperBundle\Services;
class Helper{
protected $url;
public function __construct(){
}
}
in ProfileTreeUserExtension.php in Dependency Injection folder
confirm that services configuration file which loaded is sevices.yml
namespace HelperBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class ProfileTreeLayoutExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
in service.yml
services:
helper.service:
class: HelperBundle\Services\Helper
Then you can call HelperService in several bundles Just
$helper = $this->container->get('helper.service');
you can also extends Helper class in another service
use HelperBundle\Services\Helper;
class AnotherService extends Helper{}
There good article about Service Container and Dependency Injection

Symfony2 and XCache. Did anyone get it working?

Using Symfony 2.0, I am trying to make it work together with XCache.
XCache is properly installed.
As for the offitial Symfony documentation, we have this XcacheClassLoader.php that should make it. As for the same documentation, we get this piece of advice:
/**
* XcacheClassLoader implements a wrapping autoloader cached in Xcache for PHP 5.3.
*
* It expects an object implementing a findFile method to find the file. This
* allows using it as a wrapper around the other loaders of the component (the
* ClassLoader and the UniversalClassLoader for instance) but also around any
* other autoloader following this convention (the Composer one for instance)
*
* $loader = new ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* $cachedLoader = new XcacheClassLoader('my_prefix', $loader);
*
* // activate the cached autoloader
* $cachedLoader->register();
*
* // eventually deactivate the non-cached loader if it was registered previously
* // to be sure to use the cached one.
* $loader->unregister();
*
* #author Fabien Potencier <fabien#symfony.com>
* #author Kris Wallsmith <kris#symfony.com>
* #author Kim Hemsø Rasmussen <kimhemsoe#gmail.com>
*
* #api
*/
If I am getting it right, the autoload.php should contain that code. This is what I am doing in autoload.php:
Normal loader is declared:
$loader = new UniversalClassLoader();
All stuff is done to that $loader, like registerNamespaces, registerPrefixes and all external dependencies are loaded to the $loader:
$loader
->registerNamespaces(
array(
'Symfony' => array(__DIR__ . '/../vendor/symfony/src',
__DIR__ . '/../vendor/bundles'),
'Sensio' => __DIR__ . '/../vendor/bundles',
'JMS' => __DIR__ . '/../vendor/bundles',
'Doctrine\\Common' => __DIR__. '/../vendor/doctrine-common/lib',
'Doctrine\\DBAL' => __DIR__
etc...
Once all the "normal" stuff is made to the $loader, I declare the $cachedLoader, just as said in the documentation:
// register classes with namespaces
$loader->add('Symfony\Component', __DIR__.'/component');
$loader->add('Symfony', __DIR__.'/framework');
$cachedLoader = new XcacheClassLoader('my_prefix', $loader);
// activate the cached autoloader
$cachedLoader->register();
// eventually deactivate the non-cached loader if it was registered previously
// to be sure to use the cached one.
$loader->unregister();
Am I getting it right? Well obviously not, because all what I get is a completely blank page in my browser and not even logs are written in case they could give me some clue.
Thank you in advance.
I didnt use XCache but i can suggest you an alternative way to increase class loading performance and as far as i can remember it is the best way of doing so.
Check Use Composer's Class Map Functionality section of the official documentation.
Refs:
By default, the Symfony2 standard edition uses Composer's autoloader in the
autoload.php file. This autoloader is easy to use, as it will automatically find
any new classes that you've placed in the registered directories.
Unfortunately, this comes at a cost, as the loader iterates over all configured
namespaces to find a particular file, making file_exists calls until it finally
finds the file it's looking for.
The simplest solution is to tell Composer to build a "class map" (i.e. a big
array of the locations of all the classes). This can be done from the command
line, and might become part of your deploy process:
php composer.phar dump-autoload --optimize
Internally, this builds the big class map array in
vendor/composer/autoload_namespaces.php.
If you aren't already at 2.2, I recommend you upgrade to 2.2 if at all possible. 2.0 is now at the end of its maintenance cycle.
But, since we have experience in both - for symfony 2.0 there isn't an XcacheClassLoader included, you have to write it yourself and then put it in app/autoload.php (in the same place the the APC loader would go). For symfony 2.2 that's no longer the case.
Next, I recommend you make sure that your xcache is working right - you may want to create a php file and do a test like this:
<?php
$something="test";
xcache_set('key', $something, 0);
if (xcache_get('key') !== $something)
echo "something's wrong...";
else
echo "xcache appears to be working";
Last, what we what we did is pretty much what's in the latest version of Symfony/web/app.php (modified for XcacheClassLoader) - something that would (in its simple form) look like this:
<?php
use Symfony\Component\ClassLoader\XcacheClassLoader;
use Symfony\Component\HttpFoundation\Request;
$loader = require_once __DIR__.'/../app/bootstrap.php.cache';
$loader = new XcacheClassLoader('sf2', $loader);
$loader->register(true);
require_once __DIR__.'/../app/AppKernel.php';
require_once __DIR__.'/../app/AppCache.php';
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel = new AppCache($kernel);
// rest of app.php
This symfony2 documentation link below should explain things (including the other poster's comment on having composer dump the autoload files (note the 'current' in the url):
http://symfony.com/doc/current/book/performance.html

JMSSerializer stand alone - Annotation does not exist, or cannot be auto-loaded

I am attempting to use JMSSerializer as a stand alone library to map JSON responses from an API to my model classes and am running into some issues.
Executing the following code results in an exception:
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use JMS\Serializer\Annotation AS JMS;
class Trii {
/**
* User ID for this session
* #JMS\SerializedName("userID")
* #JMS\Annotation(getter="getUserId")
* #JMS\Type("string")
* #var string
*/
private $userId;
public function getUserId() {
return $this->userId;
}
public function setUserId($userId) {
$this->userId = $userId;
}
}
$serializer = \JMS\Serializer\SerializerBuilder::create()->setDebug(true)->build();
$object = $serializer->deserialize('{"userID":"Trii"}', 'Trii', 'json');
var_dump($object);
?>
Here is the exception
Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The annotation "#JMS\Serializer\Annotation\SerializedName" in property Trii::$userId does not exist, or could not be auto-loaded.
I have the following libraries installed for the project via composer
{
"require": {
"jms/serializer": "1.0.*#dev"
}
}
Is there something obvious I am missing since I am not using the whole Doctrine 2 solution?
EDIT: my final solution was to create a bootstrap file with the following content:
<?php
// standard composer install vendor autoload magic
require dirname(__DIR__) . '/vendor/autoload.php';
// Bootstrap the JMS custom annotations for Object to Json mapping
\Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation',
dirname(__DIR__).'/vendor/jms/serializer/src'
);
?>
Pretty sure this enables silent auto-loading which is much more convenient than registering the namespaces yourself.
AnnotationRegistry::registerLoader('class_exists');
I ran into the same problem and found your question through Google. Unfortunately you hadn't yet received any answers, so I had to dig in myself. :P
The thing is, Doctrine Annotations, which JMSSerializer Annotations uses, does NOT use normal PHP autoloading.
How are these annotations loaded? From looking at the code you could guess that the ORM Mapping, Assert Validation and the fully qualified annotation can just be loaded using the defined PHP autoloaders. This is not the case however: For error handling reasons every check for class existence inside the AnnotationReader sets the second parameter $autoload of class_exists($name, $autoload) to false. To work flawlessly the AnnotationReader requires silent autoloaders which many autoloaders are not. Silent autoloading is NOT part of the PSR-0 specification for autoloading.
This means you have to register the Annotation file(s) yourself:
AnnotationRegistry::registerFile(
<PROJECT ROOT> .
"/vendor/jms/serializer/src/JMS/Serializer/Annotation/SerializedName.php");
... or the whole namespace (preferred method):
AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation',
<PROJECT ROOT> . "/vendor/jms/serializer/src");
Be careful with the path in registerAutoloadNamespace. I first tried to register the whole path to annotations in the same manner with registerFile:
<PROJECT ROOT> . "/vendor/jms/serializer/src/JMS/Serializer/Annotation
but that failed miserably. :D
I hope this gets you a step further. :)
#SirArturio has the correct answer to this Autoloading puzzle, and I just wanted to add a touch more clarity in response to #messified or anyone else struggling to get this working. As he eloquently explained, the automatic PSR-0 handler in composer, or SPL isn't going to cut it for loading these annotations since they use Doctrine's autoloading.
So here is small complete example. Whenever you create your JMS Serializer object to begin serialization is a good time to add the annotation namespace to doctrine's autoloader. For clarity sake I'm assuming no IoC, and fully qualified namespaces (hint hint, use dependency injection):
<?php
Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation',
$your_app_basepath . "/vendor/jms/serializer/src");
$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$json_output = $serializer->serialize('MyProject\MyClass', 'json');
Then in your MyProject\MyClass:
<?php
use JMS\Serializer\Annotation as JMS;
class MyClass{
/** #JMS\Exclude */
private $something_secret;
}
And that should cut it, autoloading the proper annotation file using doctrine instead of composer.
Check the capitalisation of your annotations. I had a similar problem when deploying from a Windows dev environment to an Ubuntu server which was caused by a typo in the case of my annotation. Windows files are case-insensitive so it works there but fails on Linux.
If you use Composer, you can get loader by specifying a path in require:
$loader = require(__DIR__ . '/../vendor/autoload.php');
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
Here is the solution
1.go to php directory then install composer
php composer-setup.php
2. go to project sdk directory
e.g.
cd /Applications/XAMPP/xamppfiles/htdocs/streetreturn/adn_sdk-php-master
update composer to install dependencies
php /Users/zakir/composer.phar update
*Note: /Users/zakir/composer.phar will be located when install composer in step 1

Categories