Custom repository for Doctrine and ZF2 - doctrine-orm

I'm using ZF2 with Doctrine and I would like to use a repository but I've this error:
The class 'Application\Repository\EventRepository' was not found in the chain configured namespaces Application\Entity
The entity:
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Application\Repository\EventRepository")
*/
class Event {
// ...
}
The repository:
namespace Application\Repository;
use Doctrine\ORM\EntityRepository;
class EventRepository extends EntityRepository
{
public function test()
{
// ...
}
}
In the controller:
$this->getEntityManager()->getRepository('Application\Repository\EventRepository')->test()
In my module.config.php, I've this:
return array(
// ...
'doctrine' => array(
'driver' => array(
'application_entities' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/Entity')
),
'orm_default' => array(
'drivers' => array(
'Application\Entity' => 'application_entities'
)
)
),
// ...
)
);
So I tried to change for:
return array(
// ...
'doctrine' => array(
'driver' => array(
'application_entities' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/Entity')
),
'application_repositories' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/Repository')
),
'orm_default' => array(
'drivers' => array(
'Application\Entity' => 'application_entities',
'Application\Repository' => 'application_repositories'
)
)
),
// ...
)
);
And I've a new error:
Class "Application\Repository\EventRepository" sub class of "Doctrine\ORM\EntityRepository" is not a valid entity or mapped super class.
What is the problem?
Thank you

Check this
Custom Repositories You must invoke
$this->getEntityManager()->getRepository('Application\Entity\Event')->test()

Yes skurty, SylarBg is right instead of calling the repository directly, you need to call the related entity through which the call is sent to the repository

Related

Configure multiple databases in zf2 with doctrine2

How can I configure (and use) multiple databases in Zend Framework 2 with doctrine 2 ? Currently I have this in my local.php:
return array(
'doctrine' => array(
'connection' => array(
// default connection name
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'data1',
'charset' => 'utf8',
'driverOptions' => array(
1002=>'SET NAMES utf8'
)
)
)
)
),
);
But I do not see a way to add a second one.
Perhaps this example might show you the way, this is how I configured two databases in my application, but I'm not using Doctrine.
DBs configuration
return array(
'doctrine' => array(
'connection1' => array(
// default connection name
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'data1',
'charset' => 'utf8',
'driverOptions' => array(1002 => 'SET NAMES utf8')
)
)
),
'connection2' => array(
// default connection name
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'data2',
'charset' => 'utf8',
'driverOptions' => array(1002 => 'SET NAMES utf8')
)
)
)
),
);
Then you need to create a DbAdapterFactory that connect to DB connection1 and another factory to connect to DB connection2:
PS: Of course the names are just examples and you can use better ones.
class Adapter1Factory implements FactoryInterface
{
/**
* Create DbAdapter
*
* #param ServiceLocatorInterface $serviceLocator
* #return DbAdapter
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('config');
$configDb1 = $config['connection1'];
$adapter = new \Zend\Db\Adapter\Adapter($configDb1);
return $adapter;
}
}
class Adapter2Factory implements FactoryInterface
{
/**
* Create DbAdapter
*
* #param ServiceLocatorInterface $serviceLocator
* #return DbAdapter
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('config');
$configDb2 = $config['connection2'];
$adapter = new \Zend\Db\Adapter\Adapter($configDb2);
return $adapter;
}
}
Now just use the specific AdapterFactory when you want to connect to certain DB.

zf2 + Doctrine a different database for each member

Each connected member of my site has its database.
Here is the doctrine config for "user_1":
return array(
'doctrine' => array(
'connection' => array(
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'user_1',
'password' => 'psswd_user_1',
'dbname' => 'database_user_1',
'charset' => 'utf8',
'driverOptions' => array (1002 => 'SET NAMES utf8'),
)),),),);
Is there a way to replace :
'user_1', 'psswd_user_1' and 'database_user_1'
with
'user_x', 'psswd_user_x' and 'database_user_x' for user_x ?
I don't know how to do that !
I'd like to avoid copying the same code for each user ...
Thank you for help
The proper way to do this might be to inject the configuration that you need when the connection is created. I couldn't find any event that you could hook into to do that, so you have to find the right service manager key to override.
With a little bit of source code digging, I found that these options are sent to a DoctrineORMModule\Options\DBALConnection instance and that this instance is created by DoctrineORMModule\Service\DBALConnectionFactory
You need to override this factory with something like this:
<?php
namespace MyModule\Service;
use DoctrineORMModule\Service\DBALConnectionFactory;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyDBALConnectionFactory extends DBALConnectionFactory
{
public function getOptions(ServiceLocatorInterface $sl, $key, $name = null)
{
$options = parent::getOptions($sl, $key, $name);
// override for everyone that needs a DBALConnection
if ($this->getOptionClass() === 'DoctrineORMModule\Options\DBALConnection') {
// set custom parameters here
// maybe fetch the current user with $sl->get('...')
$params = [/* ... */];
$options->setParams($params);
}
return $options;
}
}
And then you just tell the service manager about it:
<?php
return [
...
'doctrine' => [
'doctrine_factories' => [
'connection' => 'MyModule\Service\DBALConnectionFactory',
]
]
...
];
Thanks to Alejandro Celaya.
1st link and 2d link
I hope it will be useful.
I know it's not perfect but I can't do better ! I'd like critical.
in config/autoload/doctrine.local.php:
'doctrine' => array(
'connection' => array(
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'gestion_toto_default',
'charset' => 'utf8',
'driverOptions' => array(1002 => 'SET NAMES utf8'),
)
),
'orm_toto_users' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
//gestion_toto_users has 2 tables : users and db_users
'dbname' => 'gestion_toto_users',
'driverOptions' => array(1002 => 'SET NAMES utf8'),
)
),
'dynamic_orm' => array(
'driverClass' =>'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => '',
'password' => '',
'dbname' => '',
'driverOptions' => array(1002 => 'SET NAMES utf8'),
),
),
),
'driver' => array(
'orm_toto_users' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\DriverChain',
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
)
),
'dynamic_orm' => array(
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
)
),
),
'entitymanager' => array(
'orm_toto_users' => array(
'connection' => 'orm_toto_users',
'configuration' => 'orm_default'
),
'dynamic_orm' => array(
'connection' => 'dynamic_orm',
),
),
'eventmanager' => array(
'orm_toto_users' => array()
),
'sql_logger_collector' => array(
'orm_toto_users' => array(),
),
'entity_resolver' => array(
'orm_toto_users' => array()
),),
module.config.php :
'doctrine' => array(
'driver' => array(
__NAMESPACE__ . '_driver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(
__DIR__ . '/../src/' . __NAMESPACE__ . '/Entity'
),
),
'orm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
)
),
),
'authentication' => array(
'orm_default' => array(
'object_manager' => 'doctrine.entitymanager.orm_toto_users',
'identity_class' => 'MyModule\Entity\User',
'identity_property' => 'identifiant',
'credential_property' => 'password',
'credential_callable' => function(\MyModule\Entity\User $user, $passwordGiven) {
$bcrypt = new \Zend\Crypt\Password\Bcrypt();
return $bcrypt->verify($psswdGiven, $user->getPsswd()) && $user->getIsactif();
},
),
),
),
in Xcontroller :
public function getEntityManager()
{
if (null === $this->em) {
$this->em = $this->getServiceLocator()->get('dynamic_entity_manager');
}
return $this->em;
}
in userController :
public function getEntityManager()
{
if (null === $this->em) {
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_toto_users');
}
return $this->em;
}
in DynamicEMFactory.php:
class DynamicEMFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator)
{
// Get current user
$authService = $serviceLocator->get('Zend\Authentication\AuthenticationService');
if (! $authService->hasIdentity()) {
throw new \RuntimeException(
'It is not possible to create a dynamic entity manager before a user has logged in'
);
}
$user = $authService->getIdentity();
$db_User = $user->getUser_db()->getDbuser();
$db_Psswd = $user->getUser_db()->getDbpsswd();
$db_Name = $user->getUser_db()->getDbname();
// Update connection config
$globalConfig = $serviceLocator->get('config');
$globalConfig['doctrine']['connection']['dynamic_orm']['params']['user'] = $db_User;
$globalConfig['doctrine']['connection']['dynamic_orm']['params']['password'] = $db_Psswd;
$globalConfig['doctrine']['connection']['dynamic_orm']['params']['dbname'] = $db_Name;
$isAllowOverride = $serviceLocator->getAllowOverride();
$serviceLocator->setAllowOverride(true);
$serviceLocator->setService('config', $globalConfig);
$serviceLocator->setAllowOverride($isAllowOverride);
return $serviceLocator->get('doctrine.entitymanager.dynamic_orm');
}
}
in module.config :
'service_manager' => array(
'factories' => array(
'dynamic_entity_manager' => 'XXX\Service\Factory\DynamicEMFactory',
in onBootstrap (to change appearence):
$authService = $serviceManager->get('Zend\Authentication\AuthenticationService');
if ($authService->getIdentity()) {
$em = $serviceManager->get('dynamic_entity_manager');
} else {
$em = $serviceManager->get('doctrine.entitymanager.orm_default');
}
$viewModel = $e->getApplication()->getMvcEvent()->getViewModel();
$query = $serviceManager->get('param_user');
$tab = $query->getReponse($em);
$nom_theme = $tab['something']));
$viewModel->nom_theme = $nom_theme;//to layout

ZF2 Doctrine 2 ObjectSelect with distinct on field

to populate my form I use the fieldset approach. For one given form field I will use a select and the options are coming directly from an entity like this:
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'city',
'options' => array(
'label' => 'City: ',
'object_manager' => $this->_om,
'target_class' => 'Hotbed\Entity\AllAdresses',
'property' => 'city',
'is_method' => true,
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('postal_code' => $postalCode),
'orderBy' => array('city' => 'ASC'),
),
),
),
'attributes' => array(
'class' => 'form-control input-large',
'required' => '*'
),
)
);
This works pretty well. The only inconvient is that I have to put a distinct on the field city. How can I solve this problem?
Regards Andrea
The way I got around this was to create a function in the repository to return the distinct entities, and then specify that function name in your form element.
So in your case, for example:
In your repository:
public function findDistinctCitiesByPostalCode($postalCode) {
$dql = "SELECT DISTINCT a.city "
. "FROM Hotbed\Entity\AllAdresses a "
. "WHERE a.postalCode :postalCode";
$qry = $this->getEntityManager()->createQuery($dql);
$qry->setParameter('postalCode', $postalCode);
$results = $qry->getArrayResult();
// $results will be an array in the format
// array(array('city' => 'city_1'), array('city' => 'city_1'),....)
// so you'll need to loop through and create an array of entities
foreach ($results as $row) {
$addresses[] = new Hotbed\Entity\AllAdresses(array('city' => $row['city']);
}
return $addresses;
}
And then in your form:
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'city',
'options' => array(
'label' => 'City: ',
'object_manager' => $this->_om,
'target_class' => 'Hotbed\Entity\AllAdresses',
'property' => 'city',
'is_method' => true,
'find_method' => array(
'name' => 'findDistinctCitiesByPostalCode'
)
)
)
);

Authentication using Doctrine 2 MongoDbODM Module

I'm trying to set a login(Authentication) using Zend2 and DoctrineODMModule but I am getting an error.
I have followed the tutorial to setup the Authentication of Zend2 with doctorineODMModule on github
any suggestion what I am doing wrong? or what I have to do?
i have done it in the following way.
in doctrine mdule.config.php
'authentication' => array(
'odm_default' => array(
'object_manager' => 'doctrine.documentmanager.odm_default',
'identity_class' => 'Admin\Document\User',
'identity_property' => 'username',
'credential_property' => 'password',
),
),
'odm_driver' => array(
'class' => 'Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver',
'paths' => array(__DIR__ . '/../src/' . __NAMESPACE__ . '/Document')
),
'odm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Document' => 'odm_driver'
)
)
in Admin/Document/User.php created two methods getUsername and getPassword.
public function getUsername(){
return $this->username;
}
public function getPassword(){
return $this->password;
}
created controller in index controller.php
public function loginAction(){
$this->layout('layout/login-layout.phtml');
$login_error=false;
$loginForm = new LoginForm();
if ($this->request->isPost())
{
$loginForm->setData($this->request->getPost());
if ($loginForm->isValid())
{
// try {
// throw new \Exception("My exception");
$data = $loginForm->getData();
$authService = $this->getServiceLocator()
->get('doctrine.authenticationservice.odm_default');
$adapter = $authService->getAdapter();
$adapter->setIdentityValue($data['username']); // i am using username
$adapter->setCredentialValue(md5($data['password']));
$authResult = $authService->authenticate();
if ($authResult->isValid()) {
$this->redirect()->toRoute('admin_index'); // or last viewed page
}
/*} catch (Exception $e) {
echo "Caught exception $e\n";
echo $e->getPrevious();
$login_error=false;
return new ViewModel(array(
'loginForm' => $loginForm,
'login_error' => $login_error,
));
//exit;
}/
return array(
'loginForm' => $loginForm,
'errors' => 'username or password is not valid',
);
$this->redirect()->toRoute('admin_index');
} else {
//
// LOG Event ( login|password not valide )
//
//Zend\Debug\Debug::dump("not valid data");
//Zend\Debug\Debug::dump($loginForm->getMessages());
$login_error=true;
}//* */
}
}
//
return new ViewModel(array(
'loginForm' => $loginForm,
'login_error' => $login_error,
));
}
With the message you give,
A value for the identity was not provided prior to authentication with ObjectRepository authentication adapter
I'd say that either you didn't give the field to use as identity on you're User Document or during authentication process (in your action) you didn't populate the value for identity (aka login)
Please give more informations about your app (odm module configuration, Identity class...) to provide you a better help
as configuration you should have stg like :
...
'doctrine' => array(
'driver' => array(
__NAMESPACE__ . '_orm_driver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/' . __NAMESPACE__ . '/Entity')
),
'orm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_orm_driver'
)
),
__NAMESPACE__ . '_odm_driver' => array(
'class' => 'Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver',
'paths' => array(__DIR__ . '/../src/' . __NAMESPACE__ . '/Document')
),
'odm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Document' => __NAMESPACE__ . '_odm_driver'
)
)
),
'authentication' => array(
'odm_default' => array(
'objectManager' => 'doctrine.documentmanager.odm_default',
'identityClass' => 'Application\Document\User',
'identityProperty' => 'username',
'credentialProperty' => 'password',
'credentialCallable' => 'Application\Utils::hashPassword' // Not needed if you don't hash passwords
),
),
),
...
This was working great for some of my projects

Doctrine2 + Zend Framework 2 - EntitiesManager as variable in Controller

I want to set EntitiesManager instance available on my Controller. I want the Instance available with $this->em on Controller. This is my configuration :
module.config.php
[...]
'doctrine' => array(
'driver' => array(
'application_entities' => array(
'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/Entity')
),
'orm_default' => array(
'drivers' => array(
'Application\Entity' => 'application_entities'
),
),
),
),
'di' => array(
'instance' => array(
'alias' => array(
'application' => 'Application\Controller\IndexController',
),
'Application\Controller\IndexController' => array(
'parameters' => array(
'em' => 'doctrine_em',
),
),
),
),
IndexController.php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use \Doctrine\ORM\EntityManager;
class IndexController extends AbstractActionController
{
protected $em;
public function setEm(EntityManager $em) {
$this->em = $em;
return $this;
}
public function indexAction() {
$user1 = $this->em->find('\Application\Entity\User', 1);
var_dump($user1->getFullname());
}
I got this error : "Fatal error: Call to a member function find() on a non-object". It seem than EntityManager is not correctly instantiate.
What Am I missing?
Thank you!
You dont need anything in the module config
You edit the Module.php, onbootstrap function
public function onBootstrap(\Zend\EventManager\EventInterface $e) {
$serviceManager = $e->getApplication()->getServiceManager();
$em = $serviceManager;
$controllerLoader = $serviceManager->get('ControllerLoader');
$controllerLoader->addInitializer(function ($controller) use ($em) {
if (method_exists($controller, 'setEm')) {
$controller->setEm($em);
}
});
}
the controller stays as you have it, with the $em field, and the setEm method
//..
protected $em;
public function setEm(EntityManager $em) {
$this->em = $em;
return $this;
}
//..