how can i get started with Doctrine 2 + ZF? any tutorials or resources?
on a side note, i hear ZF2 will use Doctrine as its models, true?
What's nice about working with ZF and Doctrine 2 is that there's very little that needs to be done to integrate them. Essentially, you just need access to an instance of Doctrine 2's EntityManager that's set up during the applications bootstrap, as well as make sure the Doctrine namespaces are getting loaded in your index.php (You'll need to use Doctrine's ClassLoader for this, Zend_Loader does not support namespaces yet).
You can instantiate your EntityManager manually in the bootstrap, or even easier, through a Resource Plugin (which makes it easy to store the database config in application.ini). You can pretty much follow the documentation on configuring and obtaining an Entity Manager in Doctrine's manual, and just have the init() method in your bootstrap resource return the instance.
You're probably going to want to depend highly on dependency injection for passing the EM to the various objects that need it. For an easy way to pass bootstrap resources into your action controllers, see this article on creating a simple resource injector.
I've been using Doctrine 2 with ZF since it's been in alpha, and have found it quite pleasant to work with.
I can give you some examples from the bootstrap.php I use:
public function _initDoctrine() {
if(PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3) {
require_once('bootstrapDoctrine.inc.php');
//things below this line are for convenience
require_once(dirname(__FILE__).'/../library/doctrinehelpers/requireEntitiesOnce.php');
Zend_Registry::set('doctrineEm', $em);
return $em;
}
}
And in the boostrapDoctrine.inc.php I have this:
use Doctrine\Common\ClassLoader,
Doctrine\ORM\Configuration,
Doctrine\ORM\EntityManager,
Doctrine\Common\Cache\ArrayCache,
Doctrine\DBAL\Logging\EchoSQLLogger;
require_once(realpath(APPLICATION_PATH . '/../library').'/doctrine2/lib/Doctrine/Common/ClassLoader.php');
$doctrineClassLoader = new ClassLoader('Doctrine', realpath(APPLICATION_PATH . '/../library').'/doctrine2/lib');
$doctrineClassLoader->register();
//no way to have your proxies generated in different directory per ZF module it seems so we use a global one
$proxiesClassLoader = new ClassLoader('Proxies', realpath(APPLICATION_PATH . '/models/doctrineproxies'));
$proxiesClassLoader->register();
/*
* #TODO make this step iterate over available modules
*/
$driverImpl = $config->newDefaultAnnotationDriver(array(APPLICATION_PATH . '/modules/mymodule1/models/doctrineentities',APPLICATION_PATH . '/modules/mymodule2/models/doctrineentities'));
$config->setMetadataDriverImpl($driverImpl);
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
// Proxy configuration
$config->setProxyDir(realpath(APPLICATION_PATH . '/models/doctrineproxies'));
$config->setProxyNamespace('Proxies');
/**
* this SQL logger is golden
* #TODO implement a switch for verbose debugging
*/
// $logger = new Doctrine\DBAL\Logging\DebugStack();
// $config->setSQLLogger($logger);
// register_shutdown_function(function($logger) {
// echo '<pre>';
// print_r($logger->queries);
// }, $logger);
$config->setAutoGenerateProxyClasses( true ); //disable in production environment
$doctrineConfig = $this->getOption('resources'); //from ini
$dbparams = $doctrineConfig['db']['params'];
$connectionOptions = array(
'driver' => $doctrineConfig['db']['adapter'],
'user' => $dbparams['username'],
'password' => $dbparams['password'],
'dbname' => $dbparams['dbname'],
'host' => $dbparams['host']
);
$em = EntityManager::create($connectionOptions, $config); //stored in zend registry later
To allow the doctrine commandline tool to function I had to create a library/doctrine2/lib/cli-config.php which is also a stripped down zend framework bootstrap. This config has the disadvantage that I have to call the doctrine cli from within this directory. Works for me ;)
/*
* #TODO make the cli-tool more flexible by better path detection
*/
// Define path to application directory
defined('APPLICATION_PATH') || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
realpath(APPLICATION_PATH . '/../../include'),
get_include_path(),
)));
/** Zend_Application */
require_once 'Zend/Application.php';
// Create application, bootstrap, and run
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$ application->getBootstrap()->bootstrap('doctrine');
$em = $application->getBootstrap()->getResource('doctrine');
/*
$configuration = new \Doctrine\Common\Cli\Configuration();
$configuration->setAttribute('em', $em);
*/
$helperSet = new \Symfony\Components\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));
Now we are all hoping for a better doctrine integration but this will only happen in ZF 2 going a big step towards namespaces like doctrine did allready.
Hope I could help.
Related
In my application I have this weird error when testing only, I am not testing any classes in my Repo folder yet but have this:
Call to undefined method Application_Model_Cache::getInstance() in C:\wamp\www\truCrowd_dev\application\models\Repo\User.php on line 12
My user Repo is:
class Application_Model_Repo_User
{
private $_database;
private $_cache ;
public function __construct()
{
$this->_database = Application_Model_Database::getInstance();
$this->_cache = Application_Model_Cache::getInstance();
$this->_longCache = Application_Model_Cache::getInstance(604800);
}
My cache class is:
class Application_Model_Cache
{
public static function getInstance($_expiration = 7200)
{
$frontend= array(
'lifetime' => $_expiration,
'automatic_serialization' => true
);
$backend= array(
'cache_dir' => './cache/',
);
$cache = Zend_Cache::factory('Output',
'File',
$frontend,
$backend
);
return $cache;
}
}
My testing bootstrap is:
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV') || define('APPLICATION_ENV', 'production');
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
I Have tried to change the Cache class by having to instantiate it but I get the same result...
My Database class has the exact structure but I haven`t gotten this in the past, now that I advanced in development I wanted to make tests and the error appeared
You should check whether Application_Model_Cache class is being mocked in any of your tests, and if the mock is not being used when calling the non existent method.
i am using Zend Framework classes in my CORE PHP project.
i am not using all features of ZF just need some classes.
i need to know How to include all Zend Classes at once so that i don't include it one by one each time i need it.
in ZF we are doing so
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
public function _initRegisterLibraries(){
$moduleLoader = new Zend_Application_Module_Autoloader(array('namespace' => '', 'basePath' => APPLICATION_PATH));
$autoLoader = Zend_Loader_Autoloader::getInstance();
return $moduleLoader;
}
how can we achieve same in CORE PHP in which i want to use some zend classes.
For me I do this in any PHP File :
define('WWW_PATH', dirname(__FILE__) . '/../..');
set_include_path(implode(PATH_SEPARATOR, array(realpath(WWW_PATH . '/library'), get_include_path())));
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Zend_');
Then I can instantiate any Zend class I want, for exemple :
$db = new Zend_Db_Adapter_Mysql();
(Just be sure the WWW_PATH point to were the library directory is currently in)
I am working with Zend_Test. Below is my tests/bootstrap.php file:
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', 'testing');
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Ca_');
Zend_Controller_Action_HelperBroker::addPrefix('Ca_Controller_Action_Helper');
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'namespace' => "Social",
'basePath' => APPLICATION_PATH . "/../library",
));
$resourceLoader->addResourceType('facebook', 'Facebook/', 'Facebook');
This is my setUp() method in my test class (which is extending Zend_Test_PHPUnit_DatabaseTestCase):
public function setUp()
{
$this->bootstrap = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
$this->entityUser = new Ca_Model_Entity_User_Registered();
$a = $this->getAdapter();
$a->setFetchMode(Zend_Db::FETCH_OBJ);
$a->query("SET FOREIGN_KEY_CHECKS=0;");
parent::setUp();
}
Now, when running a unit test I get the following error for this particular line of code:
$bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');
$options = $bootstrap->getOptions();
Fatal error: Call to a member function getOptions() on a non-object
Does anyone know what is causing the issue and how I can resolve it ?
Thanks.
It looks like your code is treating a single parameter as an object.
From the docs at: http://framework.zend.com/manual/en/zend.controller.front.html
getParam($name) allows you to retrieve a single parameter
at a time, using $name as the identifier.
I fixed it. Ended up loading the application config in bootstrap and storing it in Zend_Registry. Much easier that way.
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', APPLICATION_ENV);
Zend_Registry::set('config', $config);
I've been using Doctrine in my project without explicitly namespacing any of my classes. This led to some problems with trying to organise my code into separate sub directories (or at least it seemed to). As such I've tried to implement namespaces in my code but I'm struggling and having tried the numerous solutions on here to no avail, I need to ask.
I've got the standard project structure:
application/
--models/
--services/
--controllers/
..etc
In my Bootstrap I've got the following (without namespaces in my code which works fine):
/**
* Initialize Doctrine
* #return Doctrine_Manager
*/
public function _initDoctrine() {
// include and register Doctrine's class loader
require_once('doctrine/Doctrine/Common/ClassLoader.php');
$autoloader = \Zend_Loader_Autoloader::getInstance();
require_once('doctrine/Doctrine/Common/ClassLoader.php');
$commonLoader = new \Doctrine\Common\ClassLoader('Doctrine\Common', 'doctrine');
$autoloader->pushAutoloader(array($commonLoader, 'loadClass'), 'Doctrine\Common');
$dbalLoader = new \Doctrine\Common\ClassLoader('Doctrine\DBAL', 'doctrine');
$autoloader->pushAutoloader(array($dbalLoader, 'loadClass'), 'Doctrine\DBAL');
$ormLoader = new \Doctrine\Common\ClassLoader('Doctrine\ORM', 'doctrine');
$autoloader->pushAutoloader(array($ormLoader, 'loadClass'), 'Doctrine\ORM');
$modelLoader = new \Doctrine\Common\ClassLoader(NULL, APPLICATION_PATH . "/models");
$autoloader->pushAutoloader(array($modelLoader, 'loadClass'), '');
// create the Doctrine configuration
$config = new \Doctrine\ORM\Configuration();
// setting the cache ( to ArrayCache. Take a look at
// the Doctrine manual for different options ! )
$cache = new \Doctrine\Common\Cache\ArrayCache;
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
// choosing the driver for our database schema
// we'll use annotations
$driver = $config->newDefaultAnnotationDriver(
APPLICATION_PATH . '/models'
);
$config->setMetadataDriverImpl($driver);
// set the proxy dir and set some options
$config->setProxyDir(APPLICATION_PATH . '/models/Proxies');
$config->setAutoGenerateProxyClasses(true);
//$config->setAutoGenerateProxyClasses(false);
$config->setProxyNamespace('App\Proxies');
// now create the entity manager and use the connection
// settings we defined in our application.ini
$connectionSettings = $this->getOption('doctrine');
$conn = array(
'driver' => $connectionSettings['conn']['driv'],
'user' => $connectionSettings['conn']['user'],
'password' => $connectionSettings['conn']['pass'],
'dbname' => $connectionSettings['conn']['dbname'],
'host' => $connectionSettings['conn']['host']
);
$entityManager = \Doctrine\ORM\EntityManager::create($conn, $config);
// push the entity manager into our registry for later use
$registry = Zend_Registry::getInstance();
$registry->entitymanager = $entityManager;
return $entityManager;
}
When I add
namespace models;
to each of my model classes and update the Bootstrap to be as follows I get an Exception "Class Application does not exist" (Application is one of my models):
$modelLoader = new \Doctrine\Common\ClassLoader('models', APPLICATION_PATH . "/models");
$autoloader->pushAutoloader(array($modelLoader, 'loadClass'), 'models');
Just for completeness, I reference that model in my controller as follows:
public function indexAction()
{
$this->_helper->layout()->title = "Your applications";
$this->_helper->layout()->description = "Create, edit and view all applications you have registered with us.";
$this->view->applicationList = $this->entityManager->getRepository("Application")->findAll();
}
What am I missing? I'm sure it's obvious but really am pulling out my hair now.
After much futher searching, I stumbled across this video (login required).
Basically, the way I solved (as per the webinar) was to namespace my entities and save them under the /library directory. Then whenever I need to use them from the actual app, I access via their namespace.
We're using ZendFramework at my workplace for our webapps. It's ok, but it lacks some of the best modern practices (like dependency injection and inversion of control, aop, etc).
For a couple of months, I've been (on my own) using Ding framework as a container for DI and AOP as a test drive. I really like it, so I'd like to bring it into our projects.
But how? So there's the question: how to properly integrate Ding in Zend Framework applications? considering ZF controllers cant be beans (as they are instantiated right from the dispatcher), how to propertly inject all dependencies in them?
P.s: Not using Zend Framework is not an option (at least in the middle term).
P.P.S: Anyone care to add "ding" as a new tag?
I'm glad Ding is helping you.
I contributed on this project and also needed to integrate with a Zend Framework application. I used Zend's application resources and plugin system to achieve this.
An application resource (you can reuse among projects)
<?php
class Application_Resource_Ding extends Zend_Application_Resource_ResourceAbstract
{
protected $_options = array(
'factory' => array(
'bdef' => array(
'xml' => array(
'filename' => array('beans.xml')
),
),
),
'cache' => array(
'proxy' => array('impl' => 'dummy'),
'bdef' => array('impl' => 'dummy'),
'beans' => array('impl' => 'dummy')
)
);
public function init()
{
// set default config dir before mergin options (cant be set statically)
$this->_options['factory']['bdef']['xml']['directories'] = array(APPLICATION_PATH .'/configs');
$options = $this->getOptions();
// parse factory properties (if set)
if (isset($options['factory']['properties'])) {
$options['factory']['properties'] = parse_ini_file(
$options['factory']['properties']
);
}
// change log4php_properties for log4php.properties (if exists)
if (isset($options['log4php_properties'])) {
$options['log4php.properties'] = $options['log4php_properties'];
unset($options['log4php_properties']);
}
$properties = array(
'ding' => $options
);
return Ding\Container\Impl\ContainerImpl::getInstance($properties);
}
}
An action helper to use inside the controllers:
<?php
class Application_Action_Helper_Ding extends Zend_Controller_Action_Helper_Abstract
{
protected $ding = null;
public function init()
{
// just once...
if (null !== $this->ding) {
return;
}
// get ding bootstrapped resource
$bootstrap = $this->getActionController()->getInvokeArg('bootstrap');
$ding = $bootstrap->getResource('ding');
if (!$ding) {
throw new Zend_Controller_Action_Exception(
'Ding resource not bootstrapped'
);
}
$this->ding = $ding;
}
public function getBean($bean)
{
return $this->ding->getBean($bean);
}
public function direct($bean)
{
return $this->getBean($bean);
}
}
In your application.ini you should add something like this (plus any extra configuration you need)
resources.frontController.actionHelperPaths.Application_Action_Helper = "Application/Action/Helper"
resources.ding.factory.properties = APPLICATION_PATH "/configs/ding.properties"
resources.ding.log4php_properties = APPLICATION_PATH "/configs/log4php.properties"
And then in your controllers, to request a bean:
$service = $this->_helper->ding('someService');
Hope this helps!