Integrating ZF/Doctrine2: Where do i put my Models/Entities & Proxy classes - php

if i do integrate Zend Framework 1.10 with Doctrine 2 where do i put my Doctrine Models/Entities and Proxies? i thought of the /application or the /library directories. if i do put in the /library directory tho, will it interfere with ZF autoloading classes from there since the classes there will be using PHP 5.3 namespaces vs PEAR style namespaces.

I'm working on an application that integrates Doctrine 2 with ZF1.10 also.You don't need to use the Doctrine auto loader at all.
1) In your application.ini file add the following line (assuming you have Doctrine installed in your library folder (same as the Zend folder):
autoloadernamespaces.doctrine = "Doctrine"
2) Create a doctrine or entitymanager resource. In your ini file:
resources.entitymanager.db.driver = "pdo_mysql"
resources.entitymanager.db.user = "user"
resources.entitymanager.db.dbname = "db"
resources.entitymanager.db.host = "localhost"
resources.entitymanager.db.password = "pass"
resources.entitymanager.query.cache = "Doctrine\Common\Cache\ApcCache"
resources.entitymanager.metadata.cache = "Doctrine\Common\Cache\ApcCache"
resources.entitymanager.metadata.driver = "Doctrine\ORM\Mapping\Driver\AnnotationDriver"
resources.entitymanager.metadata.proxyDir = APPLICATION_PATH "/../data/proxies"
resources.entitymanager.metadata.entityDir[] = APPLICATION_PATH "/models/entity"
3) Next, you will need to bootstrap it. I added a resource class in my resources folder. Make sure you map to the folder in your ini file:
pluginPaths.Application_Resource_ = APPLICATION_PATH "/resources"
Then your resource class...
class Application_Resource_EntityManager
extends Zend_Application_Resource_ResourceAbstract
{
/**
* #var Doctrine\ORM\EntityManager
*/
protected $_em;
public function init()
{
$this->_em = $this->getEntityManager();
return $this->_em;
}
public function getEntityManager()
{
$options = $this->getOptions();
$config = new \Doctrine\ORM\Configuration();
$config->setProxyDir($options['metadata']['proxyDir']);
$config->setProxyNamespace('Proxy');
$config->setAutoGenerateProxyClasses((APPLICATION_ENV == 'development'));
$driverImpl = $config->newDefaultAnnotationDriver($options['metadata']['entityDir']);
$config->setMetadataDriverImpl($driverImpl);
$cache = new Doctrine\Common\Cache\ArrayCache();
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
$evm = new Doctrine\Common\EventManager();
$em = Doctrine\ORM\EntityManager::create($options['db'],$config,$evm);
return $em;
}
}
The doctrine 2 entity manager is now available to your application. In your controller you can grab it like so:
$bootstrap = $this->getInvokeArg('bootstrap');
$em = $bootstrap->getResource('entitymanager');
I am sure this will help somebody :)

In theory, you could put then anywhere, as long as the namespaces resolve correct.
I would suggest this structure:
/application/models/MyApp/Entities
/application/models/MyApp/Proxies
Load the 'MyApp' using Doctrine's ClassLoader. I've had no conflicts using the Doctrine loader with the Zend Loader (if you have classes that use the PEAR convention inside your namespace folder, you will still need to use the Zend Loader).
Remember that 'models' can be more than just your Entity classes. My model layer consists of interfaces, factories, validators and service objects. To that end, anything that is application specific business logic should probably go in the model folder.

I would put the Models in the same directory where the "normal" Zend Framework models life. /models
You can tell Doctrine to generate the models at this place, and prefix them with "Default_Model" or whatever.
Check out one of John Lebenshold Screencasts about Zend Framework and Doctrine
Zend Screencasts

Related

How to register new Twig Namespaces from inside a Bundle?

I search for a way to change the configuration of the Symfony twig bundle in order to register additional twig namespaces.
I tripped over prepend extension but this would just solve the problem half way. When I write code like this:
public function prepend(ContainerBuilder $container)
{
$bundles = $container->getParameter('kernel.bundles');
if (isset($bundles['TwigBundle'])) {
$config = $container->getExtensionConfig('twig')[0];
$paths = ['/path/to/cms' => 'cms'];
if (array_key_exists('path', $config)) {
$paths = array_merge($config['paths'], $paths);
}
$config['paths'] = $paths;
$container->prependExtensionConfig('twig', $config);
}
}
This would mean that no other bundle could ever change the twig configuration again:
How to add twig namespaces from inside a bundle without obstructing a way for other bundles to do the same?
You can do this by creating a Compiler Pass within your Bundle:
class CustomCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if ($container->hasDefinition('twig.loader.native_filesystem')){
$bundleDirectory = \dirname(__DIR__,2);
$twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.native_filesystem');
$twigFilesystemLoaderDefinition->addMethodCall('addPath', [$bundleDirectory.'/templates/admin', 'admin']);
}
}
}
Twig namespaces are an application concern, not a bundle concern.
Registering new namespaces from within the bundle could clash with already defined namespaces by the application that consumes the bundle.
Furthermore, it's not necessary, because Symfony already auto-crates a namespace for each bundle:
If you install packages/bundles in your application, they may include their own Twig templates (in the Resources/views/ directory of each bundle). To avoid messing with your own templates, Symfony adds bundle templates under an automatic namespace created after the bundle name.
For example, the templates of a bundle called AcmeFooBundle are available under the AcmeFoo namespace. If this bundle includes the template <your-project>/vendor/acmefoo-bundle/Resources/views/user/profile.html.twig, you can refer to it as #AcmeFoo/user/profile.html.twig.
You shouldn't mess with that configuration. Leave it up to the application developer or the framework to deal with it.

ZF2 Model used in many modules using zend global config

I have a Class object which is used in many modules in my zend structure :
/module/
--|Aplication
--|MyClassModule
----|config
----|src
------|Factory
------|Model
---------|> MyObjectClass.php
----Module.php
--|AnotherModule
So my idea is to use this MyObjectClass.php in other modules so I can avoid duplication and have its own configuration. So far, for this is ok, however I want to get the variables set from my config/autoload files injected in this class but I don't know how.
How can I load this config data into my class model? Which is the best approach ? I can load it by accessing this directly but I don't think this is very elegant
e.g: $configArray = require './config/autoload/config.local.php';
I am not very experienced with zend so I dont know where to start with. I have seen many tutorials of how to do this via controllers, views.. etc but not in specific classes.
Thank you.
All config files are merged into one config, when your ZF2 application is bootstrapped. That includes local.php, global.php from config/autoload and all used modules' module.config.php. With a bit of more research, you can overwrite the standard loading, e.g. loading custom configs.
After bootstrapping, your are able to access the config from the ServiceManager. There are preserved keys for some ZF2-specific configs, service_manager, etc.
$serviceManager->get('config');
There is a "standard" service pattern in ZF2: Factory. This can be applied for Controllers, Services. What ever you want.
namespace Application\Factory;
use Application\Model\MyObjectClass;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyObjectFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
// get some config parameter, inject it into model
$config = $serviceLocator->get('config');
$myObjectClass = new MyObjectClass();
// ... e.g. $myObjectClass->setConfig($config);
return $myObjectClass;
}
}
It should be clear, what this factory is made for: create and return an instance of your custom object ;) You may configure your instance with some config params. With ServiceLocator as method param, you are able to access the config, other services etc.
Further, you have to register your own service/factory in the factories section of service_manager config in your module's module.config.php:
return array(
'service_manager' => array(
'factories' => array(
'MyObjectFactory' => 'Application\Factory\MyObjectFactory',
),
),
);
Now you should be able to access your factory, e.g. in an ActionController or wherever you have access to ServiceManager. That means, you can also access this factory from different modules.
public function someCustomAction() {
$myObjectClass = $this->getServiceLocator()->get('MyObjectFactory');
$myObjectClass2 = $this->getServiceLocator()->get('MyObjectFactory');
var_dump($myObjectClass);
var_dump($myObjectClass2);
if ($myObjectClass === $myObjectClass2) {
echo '<br />equal';
}
$myObjectClass = new MyObjectClass();
$myObjectClass2 = new MyObjectClass();
var_dump($myObjectClass);
var_dump($myObjectClass2);
}
Note:
Be aware, that ServiceManager returns the same instance of your object. So, that seems like what you ask for? In contrast, creating a new instance will create different objects.
Note 2:
Tested with ZF2 v2.4.9

Access ZF2 module service from non-Zend application

I am building a Zend application with a number of modules. I would like to reuse some of these modules within other projects which are not using the Zend Framework.
I have used included the init_autoloader.php in a non-Zend test script and can access the Zend Framework classes as expected.
I can also set up my autoloader to load classes from my module directly.
What I'm aiming to do is to load the module and access its services from its Module class from the getServiceConfig() function.
Using ModuleManager, I have set up DefaultListeners and can load the module.
use Zend\ModuleManager\Listener;
use Zend\ModuleManager\ModuleManager;
chdir(dirname(__DIR__));
// Instantiate and configure the default listener aggregate
$listenerOptions = new Listener\ListenerOptions(array(
'module_paths' => array(
'./module',
'./vendor'
)
));
$defaultListeners = new Listener\DefaultListenerAggregate($listenerOptions);
// Instantiate the module manager
$moduleManager = new ModuleManager(array(
'Group',
));
// Attach the default listener aggregate and load the modules
$moduleManager->getEventManager()->attachAggregate($defaultListeners);
$moduleManager->loadModules();
What I would like to do is instantiate an instance of ServiceManager and be able to get the services from the module as I can within the application.
Any help would be much appreciated.
Thanks.
You can create just the service manager like so:
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
/* Insert your code to setup whatever autoloader you want */
$configuration = include 'config/application.config.php';
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $configuration);
$serviceManager->get('ModuleManager')->loadModules();
If you want to only use a specific module(s) then create a copy of your config/application.config.php that will only include the module(s) you want. Note, you can just place the config array directly into $configuration in the code above rather than including a file if you desire.

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

Autoloading routing from Symfony 2 bundles

Symfony 2 bundles have a nice feature for autoloading/extending the application configuration files, such as services.yml. However, this is not true for routing, since i have to manually edit the routing.yml of my application in order to load the routing data from my Bundle (the Controller or the routing.yml itself).
Is it possible to load such routing configuration this seamlessly?
---- EDIT
I ended up doing this, but it's ugly as hell:
<?php
use Symfony\Component\Routing\RouteCollection;
$collection = new RouteCollection();
foreach (glob(__DIR__.'/../../src/Vendor/MySystem/Plugins/*Bundle/Controller/', GLOB_ONLYDIR) as $controller) {
$controller = str_replace(__DIR__.'/../../src/Vendor/MySystem/Plugins/', '', $controller);
$collection->addCollection($loader->import("#$controller"));
}
return $collection;
i think you should look after the "routing.loader" dependency injection tag
It let you define a class to define routes with your logic
http://symfony.com/doc/current/reference/dic_tags.html#routing-loader
I think You could also define an dependencyInjection extension in your bundle.
In your load method, you can alter the container definitions and so your routes..

Categories