When I try to create a doctrine crud for an entity I get an 'Unknown entity namespace alias' exception.
I have the following project structure
With a series of bundles (in the Bundles directory) using the Entities in the src\Project\Entity directory.
As you can see my entity namespace is
namespace Project\Entity;
I have a feeling it might be to do with the auto_mapping, but I have been playing with this for 4-5 hours and am getting nowhere.
Any suggestions?
Solution:
Create a command which extends the base doctrine crud command
extends \Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand
Modifying
$entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace('Project').'\\'.$entity;
to the namespace of the entity. By default it assumes the entity is in the Bundle where you want the CRUD to be created.
By setting
$this->setName('project:generate:crud');
in the Configre() function you can call the function from your command line
Example:
<?php
namespace Project\Bundle\UtilityBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Sensio\Bundle\GeneratorBundle\Command\Validators;
class GenerateDoctrineCrudCommand extends \Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand
{
protected function configure()
{
parent::configure();
$this->setName('project:generate:crud');
$this->setDescription('CRUD generator that supports entities outside a bundle');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$questionHelper = $this->getQuestionHelper();
if ($input->isInteractive()) {
$question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true);
if (!$questionHelper->ask($input, $output, $question)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
// Note: this expects an argument like InterpracCorporateFrontendBundle:Notification
list($bundle, $entity) = explode(':', $input->getOption('entity'));
$format = Validators::validateFormat($input->getOption('format'));
$prefix = $this->getRoutePrefix($input, $entity);
$withWrite = $input->getOption('with-write');
$forceOverwrite = $input->getOption('overwrite');
$questionHelper->writeSection($output, 'CRUD generation');
$entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace('Project').'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle);
$generator = $this->getGenerator($bundle);
$generator->generate($bundle, $entity, $metadata[0], $format, $prefix, $withWrite, $forceOverwrite);
$output->writeln('Generating the CRUD code: <info>OK</info>');
$errors = array();
$runner = $questionHelper->getRunner($output, $errors);
// form
if ($withWrite) {
$output->write('Generating the Form code: ');
if ($this->generateForm($bundle, $entity, $metadata)) {
$output->writeln('<info>OK</info>');
} else {
$output->writeln('<warning>Already exists, skipping</warning>');
}
}
// routing
if ('annotation' != $format) {
$runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format, $entity, $prefix));
}
$questionHelper->writeGeneratorSummary($output, $errors);
}
}
The problem is that you are keeping your entities outside bundles. Since this is not standard behaviour, you have to extend or create another GenerateDoctrineCrudCommand to be able to pass namespace alias.
Related
I want to rewrite onceBasic() of Illuminate\Auth\SessionGuard by extending Facades\Auth, but encountering the problem of not being able to access the helper protected function of SessionGuard class.
Here is how I extends Auth facades:
namespace App\MyFacades;
use \Illuminate\Support\Facades\Auth;
class Auth_QuyLe extends Auth {
/**
* Create your custom methods here...
*/
public static function onceBasic_QuyLe($field = 'name', $extraConditions = [])
{
$credentials = self::basicCredentials(self::getRequest(), $field);
if (! self::once(array_merge($credentials, $extraConditions))) {
return self::failedBasicResponse();
}
}
}
When I call onceBasic_QuyLe() from my middleware, it shows
"Method Illuminate\Auth\SessionGuard::basicCredentials does not exist."
I've already updated my class to config/app.php and run composer dump-autoload && php artisan config:cache
I've finally found a solution:
Firstly, b/c SessionGuard is a “macroable” class, and if you need to add an additional method to SessionGuard , you need to go to AppServiceProvider and declare macros into the service provider’s boot method
public function boot()
{
SessionGuard::macro('myOnceBasic', function ($field = 'name', $extraConditions = []) {
$credentials = $this->basicCredentials($this->getRequest(), $field);
if (!$this->once(array_merge($credentials, $extraConditions))) {
return$this->failedBasicResponse();
}
});
}
and in the underlying class that your newly created Facades represents , you can use Auth facade to call the macros:
class MySessionGuard{
public function myOnceBasic($field = 'name', $extraConditions = [])
{
Auth::myOnceBasic($field = 'name', $extraConditions = []);
}
}
I am trying to plug APCu into the Symfony 3.3 test project.
I am getting an error, when I add ApcuAdapter to AppKernel.php.
Here is the list of what I have done:
in ./app/AppKernel.php i have added a "new" line to $bundles in public function registerBundles():
public function registerBundles()
{
$bundles = [
... ,
new Symfony\Component\Cache\Adapter\ApcuAdapter()
];
...
return $bundles;
}
Opened the project's site. It shows an error:
Attempted to call an undefined method named "getName" of class "Symfony\Component\Cache\Adapter\ApcuAdapter".
(./ means the root folder of the project)
Please, tell me why does this error happen and how to plug this adapter into the symfony framework. Thank you.
me have found the solution somewhere on the framework's website.
somehow, we should use not the Adapter, but the Simple instead.
seems very un-logical to me.
so, the Service now works and looks this way:
<?php
namespace AppBundle\Service;
use Symfony\Component\Cache\Simple\ApcuCache;
class ApcuTester
{
public function __construct
(
)
{
}
public function testMe()
{
$cache = new ApcuCache();
$TestVar_dv = 0;
$TestVar_vn = 'TestVar';
$TestVar = NULL;
//$cache-> deleteItem($TestVar_vn); // dbg
// Read
if ( $cache->hasItem($TestVar_vn) )
{
$TestVar = $cache->get($TestVar_vn);
}
else
{
$cache->set($TestVar_vn, $TestVar_dv);
$TestVar = $TestVar_dv;
}
// Modify
$TestVar++;
// Save
$cache->set($TestVar_vn, $TestVar);
// Return
return $TestVar;
}
}
And the Controller which executes this Service looks as this:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Service\MessageGenerator;
use AppBundle\Service\ApcuTester;
class LuckyController extends Controller
{
/**
* #Route("/lucky/number", name="lucky")
*/
public function numberAction
(
Request $request,
MessageGenerator $messageGenerator,
ApcuTester $apcuTester
)
{
$lucky_number = mt_rand(0, 100);
$message = $messageGenerator->getHappyMessage();
$testvar = $apcuTester->testMe();
$tpl = 'default/lucky_number.html.twig';
$tpl_vars =
[
'lucky_number' => $lucky_number,
'message' => $message,
'testvar' => $testvar
];
return $this->render($tpl, $tpl_vars);
}
}
If i wrote the same thing in pure PHP i would have done it an hour earlier :) Oh these crazy frameworks...
I wanted to use Doctrine in my project, but I am not able to use Entity Manager.
I have created entites, repositories, config files and dbconnect but it seems that it's not done correctly.
Can you please check this code? Maybe I'm missing something really small.
My dbconnect file(it is bootstrapped in init.php):
<?php
namespace Projekt\Config;
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
$paths = array("Entity");
$isDevMode = false;
// the connection configuration
$dbParams = array(
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => '',
'dbname' => 'projekt',
);
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, null, null, false);
$em = EntityManager::create($dbParams, $config);
My Repository example:
<?php
namespace Projekt\Repository;
use Doctrine\ORM\EntityRepository;
/**
* Message
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class Message extends EntityRepository
{
public function getMessage($id)
{
$message = $this->find($id);
return $message;
}
public function getAllMessages()
{
}
public function createMessage()
{
}
public function updateMessage()
{
}
public function deleteMessage()
{
}
}
Now when I'm trying to access a default or custom repository method I get this error:
Warning: Missing argument 1 for Doctrine\ORM\EntityRepository::__construct(),
called in F:\xampp\htdocs\mvc\app\Controllers\Messages.php
on line 15 and defined in F:\xampp\htdocs\mvc\vendor\doctrine\orm\lib\Doctrine\ORM\EntityRepository.php on line 64
line 64 in EntityRepository.php is a __construct function that declares entitymanager, but it seems to not be working properly:
public function __construct($em, Mapping\ClassMetadata $class)
{
$this->_entityName = $class->name;
$this->_em = $em;
$this->_class = $class;
}
Two things that i noticed:
Your path is relative. Im not sure but i always use complete path to the Entity folder. You can use __DIR__ to achieve that easily. Depending on your namespace it should look Like:
$paths = array(__DIR__ . "/../Repository");
Doctrine needs to know where to find your entities and repositories. Depending on your namespace i would think your Repository file exists in a folder named "Repository" and not "Entity".
Have you correctly defined an Entity Class? Your Repository class looks ok to me but it can only work if you have a valid Entity class.
You should not name your repository "Message". The Entity should be named "Message" and the repository should be named "MessageRepository".
I'm using Symfony forms (v3.0) without the rest of the Symfony framework. Using Doctrine v2.5.
I've created a form, here's the form type class:
class CreateMyEntityForm extends BaseFormType {
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('myEntity', EntityType::class);
}
}
When loading the page, I get the following error.
Argument 1 passed to
Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() must be
an instance of Doctrine\Common\Persistence\ManagerRegistry, none
given, called in /var/www/dev3/Vendor/symfony/form/FormRegistry.php on
line 85
I believe there's some configuration that needs putting in place here, but I don't know how to create a class that implements ManagerRegistryInterface - if that is the right thing to do.
Any pointers?
Edit - here is my code for setting up Doctrine
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Setup;
class Bootstrap {
//...some other methods, including getCredentials() which returns DB credentials for Doctrine
public function getEntityManager($env){
$isDevMode = $env == 'dev';
$paths = [ROOT_DIR . '/src'];
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, null, null, false);
$dbParams = $this->getCredentials($env);
$em = EntityManager::create($dbParams, $config);
return $em;
}
}
Believe me, you're asking for trouble!
EntityType::class works when it is seamsly integrated to "Symfony" framework (there's magic under the hoods - via DoctrineBundle). Otherwise, you need to write a lot of code for it to work properly.
Not worth the effort!
It's a lot easier if you to create an entity repository and inject it in form constructor, then use in a ChoiceType::class field. Somethink like this:
<?php
# you form class
namespace Application\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class InvoiceItemtType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('product', ChoiceType::class, [
'choices' => $this->loadProducts($options['products'])
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['products' => [],]); # custom form option
}
private function loadProducts($productsCollection)
{
# custom logic here (if any)
}
}
And somewhere in application:
$repo = $entityManager->getRepository(Product::class);
$formOptions = ['products' => $repo->findAll()];
$formFactory = Forms::createFormFactory();
$formFactory->create(InvoiceItemtType::class, new InvoiceItem, $formOptions);
That's the point!
Expanding on the answer by xabbuh.
I was able to implement EntityType in the FormBuilder without too much extra work. However it does not work with the annotations in order to use Constraints directly inside the entity, which would require a lot more work.
You can easily facilitate the ManagerRegistry requirement of the Doctrine ORM Forms Extension, by extending the existing AbstractManagerRegistry and making your own container property within the custom ManagerRegistry.
Then it's just a matter of registering the Form extension just like any other extension (ValidatorExtension, HttpFoundationExtension, etc).
The ManagerRegistry
use \Doctrine\Common\Persistence\AbstractManagerRegistry;
class ManagerRegistry extends AbstractManagerRegistry
{
/**
* #var array
*/
protected $container = [];
public function __construct($name, array $connections, array $managers, $defaultConnection, $defaultManager, $proxyInterfaceName)
{
$this->container = $managers;
parent::__construct($name, $connections, array_keys($managers), $defaultConnection, $defaultManager, $proxyInterfaceName);
}
protected function getService($name)
{
return $this->container[$name];
//alternatively supply the entity manager here instead
}
protected function resetService($name)
{
//unset($this->container[$name]);
return; //don't want to lose the manager
}
public function getAliasNamespace($alias)
{
throw new \BadMethodCallException('Namespace aliases not supported');
}
}
Create the Form
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('field_name', EntityType::class, [
'class' => YourEntity::class,
'choice_label' => 'id'
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => YourAssociatedEntity::class]);
}
}
Configure the Form Builder to use the extension and use the Form
$managerRegistry = new \ManagerRegistry('default', [], ['default' => $entityManager], null, 'default', 'Doctrine\\ORM\\Proxy\\Proxy');
$extension = new \Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension($managerRegistry);
$formBuilder = \Symfony\Component\Form\FormFactoryBuilder::createFormFactoryBuilder();
$formBuilder->addExtension($extension);
$formFactory = $formBuilder->getFormFactory();
$form = $formFactory->create(new \UserType, $data, $options);
The above is intended for demonstration purposes only! While it does
function, it is considered best
practice to
avoid using Doctrine Entities inside of Forms. Use a DTO (Data
Transfer Object) instead.
ENTITIES SHOULD ALWAYS BE VALID
INVALID STATE SHOULD BE IN A DIFFERENT OBJECT
(You may need a DTO)
(Also applies to Temporary State)
AVOID SETTERS
AVOID COUPLING WITH THE APPLICATION LAYER
FORM COMPONENTS BREAK ENTITY VALIDITY
BOTH SYMFONY\FORM AND ZEND\FORM ARE TERRIBLE
(For this use-case)
Use a DTO instead
Doctrine 2.5+ "NEW" Operator Syntax
class CustomerDTO
{
public function __construct($name, $email, $city, $value = null)
{
// Bind values to the object properties.
}
}
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city) FROM Customer c JOIN c.email e JOIN c.address a');
$users = $query->getResult(); // array of CustomerDTO
The easiest way to solve your issue is by registering the DoctrineOrmExtension from the Doctrine bridge which makes sure that the entity type is registered with the needed dependencies.
So basically, the process of bootstrapping the Form component would look like this:
// a Doctrine ManagerRegistry instance (you will probably already build this somewhere else)
$managerRegistry = ...;
$doctrineOrmExtension = new DoctrineOrmExtension($managerRegistry);
// the list of form extensions
$extensions = array();
// register other extensions
// ...
// add the DoctrineOrmExtension
$extensions[] = $doctrineOrmExtension;
// a ResolvedFormTypeFactoryInterface instance
$resolvedTypeFactory = ...;
$formRegistry = new FormRegistry($extensions, $resolvedTypeFactory);
I have created a View Helper to display latest Adverts from a Database Table. Since I have different Types of Adverts, I would like to be able to pass a variable from inside my View where I call the View Helper to show specific Adverts.
I am sorry that I can not explain it in a better way, but I am still a total beginner in ZF2. I will add the Sourcecode and hopefully this will make it more clear. Please note that I have the Sourcecode from a Book which displayed Pizza's randomly and changed it till it worked to show my adverts. I might still have Code in it which is not actually needed, so please do not wonder... Okay here the code:
1. the view: index.html
<?php foreach ($this->latestAdvert() as $value){ ?>
<li><?php echo $value->getAdvertTitle();?></li>
<?php }?>
2. the view Helper: Advert\View\Helper\LatestAdvert.php
namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LatestAdvert extends AbstractHelper
{
protected $random = null;
public function __construct($random)
{
$this->setLatestAdvert($random);
}
public function setLatestAdvert($random)
{
$this->random = $random;
}
public function getLatestAdvert()
{
return $this->random;
}
public function __invoke()
{
$latestAdverts = $this->getLatestAdvert();
return $latestAdverts;
}
}
3. the Factory: Advert\View\Helper\LatestAdvertFactory.php
namespace Advert\View\Helper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class LatestAdvertFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$locator = $serviceLocator->getServiceLocator();
$service = $locator->get('Advert\Service');
$random = $service->fetchSingleByRandom();
$helper = new LatestAdvert($random);
return $helper;
}
}
4. the Service: Advert\Service\LatestAdvertService .php
namespace Advert\Service;
use Advert\Entity\Advert as AdvertEntity;
use Doctrine\ORM\EntityManager;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\Debug\Debug;
class LatestAdvertService implements ServiceManagerAwareInterface
{
/**
* Service manager.
* #var Zend\ServiceManager\ServiceManager
*/
private $serviceManager = null;
/**
* Sets service manager.
* #param Zend\ServiceManager\ServiceManager $serviceManager Service manager.
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
}
/**
* Returns service manager.
* #return type
*/
public function getServiceLocator()
{
return $this->serviceManager;
}
public function fetchSingleByRandom()
{
// Get Doctrine entity manager.
$entityManager = $this->getServiceLocator()
->get('doctrine.entitymanager.orm_default');
$advertType = 'wanted'; // This should be removed
$random = $entityManager->getRepository('Advert\Entity\Advert')
->findAdvertsByDate($advertType);
return $random;
}
}
5. Module: Advert\Module.php
public function getServiceConfig()
{
return array(
'invokables' => array(
'Advert\Service' => 'Advert\Service\LatestAdvertService',
),
);
}
public function getViewHelperConfig()
{
return array(
'factories' => array(
'latestAdvert' => 'Advert\View\Helper\LatestAdvertFactory',
),
);
}
As you can see in #4 I have a Variable called $advertType. I want to set the variable when I call the view Helper in my index.html, f.e. $this->latestAdvert('wanted'), but how can I pass this variable through all my files? I just can not find a solution for it. Does anyone got a tip for me how to achieve it? Thank you very much in advance.
!UPDATE!
As SenseException pointed out below, that injecting a service locator into a service is a bad practice and instead I should either inject repository or entity manager into the service, I have now worked out the first working solution for the entity manager.
For that I have updated 2 Files: module.php and LatestAdvertService.php
#5 module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'Advert\Service' => function ($sl) {
$entityManager = $sl->get('doctrine.entitymanager.orm_default');
$myService = new Service\LatestAdvertService();
$myService->setEntityManager($entityManager);
//or you can set repository
//$repository = $entityManager->getRepository('Advert\Entity\Advert');
//$myService->setRepository($repository);
return $myService;
},
4. the Service: Advert\Service\LatestAdvertService .php
namespace Advert\Service;
use Advert\Entity\Advert as AdvertEntity;
use Doctrine\ORM\EntityManager;
class LatestAdvertService
{
public function setEntityManager(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function setRepository(Repository $repository) {
$this->repository = $repository;
}
public function fetchSingleByAdvertType($advertType)
{
$random = $this->entityManager->getRepository('Advert\Entity\Advert')->findAdvertsByDate($advertType);
// $random = $this->repository->findAdvertsByDate($advertType);
return $random;
}
}
I have tried to inject the repository but get the following error message:
Argument 1 passed to Advert\Service\LatestAdvertService::setRepository() must be an instance of Advert\Service\AdvertRepository, instance of Advert\Repository\AdvertRepository given, called in
I will continue to find a solution for the repository injection and update when successful.
How about this solution:
namespace Advert\View\Helper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class LatestAdvertFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$locator = $serviceLocator->getServiceLocator();
$service = $locator->get('Advert\Service');
$helper = new LatestAdvert($service);
return $helper;
}
}
And of course the helper class:
namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LatestAdvert extends AbstractHelper
{
protected $service;
public function __construct($service)
{
$this->service = $service;
}
public function __invoke($advertType)
{
$latestAdverts = $this->service->fetchSingleByAdvertType($advertType);
return $latestAdverts;
}
}
And for the service:
public function fetchSingleByAdvertType($advertType)
{
$entityManager = $this->getServiceLocator()
->get('doctrine.entitymanager.orm_default');
$random = $entityManager->getRepository('Advert\Entity\Advert')
->findAdvertsByDate($advertType);
return $random;
}
I tried to keep your code as close to your original as possible but please hear some suggestions about the service locator. It is a bad practice to inject a service locator into a service like you did in LatestAdvertService. Since you only need a repository for your service, just inject that one into it. If you need the entity manager in your service, inject it instead. Your unittests will thank you.