I'm getting the error invalid argument key "articleBuilder", when I launch a console command from symfony3.
I have extended an abstract service, and I'm including the configurations and the classes code that I have used.
Given as the configuration of my parent class service:
app.service.user.access_controller:
abstract: true
class: '%app.service.user.access_controller.class%'
arguments:
currentUser: "#=service('security.token_storage').getToken().getUser()"
userRoleProvider: '#app.repository.user.user_role_repository'
Where this is the parent service class:
<?php
namespace AppBundle\Service\User;
use AppBundle\Exception\User\AccessControlException;
use AppBundle\ModelInterface\User\OwnableInterface;
use AppBundle\ModelInterface\User\UserInterface;
use AppBundle\ServiceInterface\User\UserRoleProviderInterface;
/**
* Class AccessController
* #package AppBundle\Service\User
*/
abstract class AccessController
{
/** #var UserInterface */
protected $currentUser;
/** #var UserRoleProviderInterface */
protected $userRoleProvider;
/**
* AccessControlledService constructor.
* #param UserInterface $currentUser
* #param UserRoleProviderInterface $userRoleProvider
*/
public function __construct(UserInterface $currentUser, UserRoleProviderInterface $userRoleProvider)
{
$this->currentUser = $currentUser;
$this->userRoleProvider = $userRoleProvider;
}
And where this is my child service definition:
app.controller.article.article_json_api_controller:
class: '%app.controller.article.article_json_api_controller.class%'
parent: 'app.service.user.access_controller'
arguments:
articleBuilder: '#app.service.article.doctrine_orm.article_entity.builder'
articlePersister: '#app.service.article.doctrine_orm.article_entity.persister'
articleProvider: '#app.repository.article.article_repository'
articleUpdater: '#app.service.article.updater'
articleDestroyer: '#app.service.article.doctrine_orm.article_entity.destroyer'
languageProvider: '#app.repository.language.language_repository'
sectionBuilder: '#app.service.article.doctrine_orm.article_section_entity.builder'
sectionUpdater: '#app.service.article.article_section.updater'
And this is my child service class:
class ArticleJsonApiController extends AccessController
{
/** #var UserInterface */
private $currentUser;
/** #var ArticleBuilderInterface */
private $articleBuilder;
/** #var ArticlePeristerInterface */
private $articlePersister;
/** #var ArticleProviderInterface */
private $articleProvider;
/** #var ArticleUpdaterInterface */
private $articleUpdater;
/** #var ArticleDestroyerInterface */
private $articleDestroyer;
/** #var LanguageProviderInterface */
private $languageProvider;
/** #var ArticleSectionBuilderInterface */
private $sectionBuilder;
/** #var ArticleSectionUpdaterInterface */
private $sectionUpdater;
/**
* ArticleJsonApiController constructor.
*
* #param UserInterface $currentUser
* #param UserRoleProviderInterface $userRoleProvider
* #param ArticleBuilderInterface $articleBuilder
* #param ArticlePeristerInterface $articlePersister
* #param ArticleProviderInterface $articleProvider
* #param ArticleUpdaterInterface $articleUpdater
* #param ArticleDestroyerInterface $articleDestroyer
* #param LanguageProviderInterface $languageProvider
* #param ArticleSectionBuilderInterface $sectionBuilder
* #param ArticleSectionUpdaterInterface $sectionUpdater
*/
public function __construct(
UserInterface $currentUser,
UserRoleProviderInterface $userRoleProvider,
ArticleBuilderInterface $articleBuilder,
ArticlePeristerInterface $articlePersister,
ArticleProviderInterface $articleProvider,
ArticleUpdaterInterface $articleUpdater,
ArticleDestroyerInterface $articleDestroyer,
LanguageProviderInterface $languageProvider,
ArticleSectionBuilderInterface $sectionBuilder,
ArticleSectionUpdaterInterface $sectionUpdater
) {
parent::__construct($currentUser, $userRoleProvider);
I believe the problem does not lie with articleBuilder itself, but rather with your intention to define the arguments as an associative array.
If I am not mistaken, when dealing with parent services, you have at your disposal a special index_N key which serves to override the value of some argument already defined in your parent service. Therefor, I think, you need to define your argument in way that looks like this:
app.controller.article.article_json_api_controller:
class: '%app.controller.article.article_json_api_controller.class%'
parent: 'app.service.user.access_controller'
arguments:
- '#app.service.article.doctrine_orm.article_entity.builder'
- '#app.service.article.doctrine_orm.article_entity.persister'
- '#app.repository.article.article_repository'
- '#app.service.article.updater'
- '#app.service.article.doctrine_orm.article_entity.destroyer'
- '#app.repository.language.language_repository'
- '#app.service.article.doctrine_orm.article_section_entity.builder'
- '#app.service.article.article_section.updater'
Hope this helps a bit...
I think you misunderstood what parent services are. AFAIK you're trying to map 1-to-1 your parent abstract class as an abstract service, but parent services are used differently in Symfony, i.e. to manage common dependencies for several classes.
Reference: How to Manage Common Dependencies with Parent Services - Symfony docs
Related
Given the code
namespace DigitalPublications\MidasWPX;
use DigitalPublications\MidasWPX\Interfaces\StyleInterface;
use DigitalPublications\MidasWPX\Interfaces\PathsManagerInterface;
/**
*
* #author Chris Athanasiadis <chris.k.athanasiadis#gmail.com>
*
*/
final class Style extends Base implements StyleInterface {
/**
*
* #inheritdoc
*
* $config parameters:
* <br>
* - pathsManager {#link PathsManagerInterface PathsManagerInterface}
*/
protected function construct(array $config = array()) {
/**
*
* #internal
* #var \DigitalPublications\MidasWPX\Interfaces\PathsManagerInterface $pathsManager
*/
$pathsManager = $this->options->get('pathsManager', $config);
}
}
The part
* - pathsManager {#link PathsManagerInterface PathsManagerInterface}
Should have created a link to the PathsManagerInterface interface, but instead Doxygen throws an error warning: unable to resolve link to 'PathsManagerInterface' for \link command.
If I use a class and not an interface, it works as expected.
UPDATE
After #albert comment, I include some other info as well:
Interface code
namespace DigitalPublications\MidasWPX\Interfaces;
/**
* Manager for paths
*
* #author Chris Athanasiadis <chris.k.athanasiadis#gmail.com>
*
*/
interface PathsManagerInterface extends ManagerInterface {
}
Operating system is macOS High Sierra
Doxygen version is 1.8.17
Settings that are changed from default values are:
OPTIMIZE_OUTPUT_FOR_C
ABBREVIATE_BRIEF
STRIP_FROM_PATH
HIDE_SCOPE_NAMES
RECURSIVE
UPDATE 2
Link to class that works.
Class
namespace DigitalPublications\MidasWPX;
use DigitalPublications\MidasWPX\Manager\Manager;
use DigitalPublications\MidasWPX\Interfaces\PathsManagerInterface;
/**
*
* #author Chris Athanasiadis <chris.k.athanasiadis#gmail.com>
*
*/
final class PathsManager extends Manager implements PathsManagerInterface {
/**
*
* #inheritdoc
*/
protected function initManager(array $managerConfig = array()) {
}
}
Comment changed to
* - pathsManager {#link PathsManager PathsManager}
I use Loggable to backup changes in Entities.
The default AbstractLogEntry does not have enough columns for my needs.
Thats why i extended the class and added extra getters and setters.
See the code below
/**
* EmployeeBackup
*
* #ORM\Table(name="employee_backup")
* #ORM\Entity(repositoryClass="Gedmo\Loggable\Entity\Repository\LogEntryRepository")
*
*/
class EmployeeBackup extends AbstractLogEntry
{
/**
* #var int
*
* #ORM\Column(name="division_id", type="integer", unique=true)
*/
private $divisionId;
/**
* #return int
*/
public function getDivisionId(): int
{
return $this->divisionId;
}
/**
* #param string $divisionId
*/
public function setDivisionId(string $divisionId): void
{
$this->divisionId = $divisionId;
}
}
The extension is using the class above. So it works.
But now i need to set the divisionId when a new version is stored.
I tried the code below
$loggable = new LoggableListener();
$loggable->setDivision($division);
$evm->addEventSubscriber($loggable);
And this is what i get:
Attempted to call an undefined method named "setDivision" of class "Gedmo\Loggable\LoggableListener".
And thats true because LoggableListener does not have a setDivision function. My question is: Do i need to override the listener and if so, how do i do that?
Thanks ;)
I am trying to build an entity object for my relationship in Neo4j database with GraphAware Neo4j PHP OGM library using this simple method:
public function getRelationshipEntity($entityId) {
$repo = $this->entityManager->getRepository( Entity\Relationship\Fired::class );
return $repo->findOneById($entityId);
}
Here we have the entity classes, relationship first:
namespace Entity\Relationship;
use GraphAware\Neo4j\OGM\Annotations as OGM;
use Entity\Issue;
use Entity\Event;
/**
* #OGM\RelationshipEntity(type="FIRED")
*/
class Fired {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #OGM\StartNode(targetEntity="Entity\Event")
*/
protected $event;
/**
* #OGM\EndNode(targetEntity="Entity\Issue")
*/
protected $issue;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $time;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $eventName;
}
Then, start node:
namespace Entity;
use GraphAware\Neo4j\OGM\Annotations as OGM;
/**
* #OGM\Node(label="Event")
*/
class Event {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $name;
}
..and end node:
namespace Entity;
use Doctrine\Common\Collections\ArrayCollection;
use GraphAware\Neo4j\OGM\Annotations as OGM;
/**
* #OGM\Node(label="Issue")
*/
class Issue {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #OGM\Property(type="string")
*/
protected $key;
/**
* #OGM\Property(type="string")
*/
protected $created;
/**
* #OGM\Property(type="string")
*/
protected $updated;
/**
* #OGM\Relationship(type="FIRED", direction="INCOMING", relationshipEntity="Entity\Relationship\Fired", collection=true)
* #var ArrayCollection
*/
protected $eventFires;
public function __construct($key) {
$this->key = $key;
$this->eventFires = new ArrayCollection();
}
public function __wakeup() {
$this->__construct($this->key);
}
/**
* #return ArrayCollection
*/
public function getEventFires() {
return $this->eventFires;
}
public function addEventFire(Entity\Relationship\Fired $eventFired) {
$this->eventFires->add($eventFired);
}
public function removeEventFire(Entity\Relationship\Fired $eventFired) {
$this->eventFires->removeElement($eventFired);
}
}
Apparently, what works really well for node entites, triggers the following error for relationships:
Fatal error: Call to undefined method GraphAware\Neo4j\OGM\Metadata\RelationshipEntityMetadata::hasCustomRepository() in /vendor/graphaware/neo4j-php-ogm/src/EntityManager.php
Any suggestion how I could workaround this? I even tried using EntityManager::createQuery() the following way:
public function getRelationships($eventName) {
$query = $this->entityManager->createQuery('MATCH (e:Event)-[f:FIRED{eventName: {eventName}}]->() RETURN e,f ORDER BY f.time');
$query->addEntityMapping('e', 'Entity\Event' );
$query->addEntityMapping('f', 'Entity\Relationship\Fired' );
$query->setParameter( 'eventName', $eventName);
return $query->execute();
}
But, apparently, addEntityMapping() doesn't work for relationship entities either! (It might be a feature though, not a bug):
Catchable fatal error: Argument 1 passed to GraphAware\Neo4j\OGM\Hydrator\EntityHydrator::hydrateNode() must implement interface GraphAware\Common\Type\Node, instance of GraphAware\Bolt\Result\Type\Relationship given, called in /vendor/graphaware/neo4j-php-ogm/src/Query.php on line 145 and defined in /vendor/graphaware/neo4j-php-ogm/src/Hydrator/EntityHydrator.php on line 232
So, I ended up that I can easily define and store relationship entities in Neo4J with this library but not sure how I could retrieve it easily with EntityManager, in the similar way I can do so with nodes.
Any help would be much appreciated!
As requested in comment below, these are GraphAware packages that I am using:
graphaware/neo4j-bolt 1.9.1 Neo4j Bolt Binary Protocol PHP Driver
graphaware/neo4j-common 3.4.0 Common Utilities library for Neo4j
graphaware/neo4j-php-client 4.8.0 Neo4j-PHP-Client is the most advanced PHP Client for Neo4j
graphaware/neo4j-php-ogm 1.0.0-RC6 PHP Object Graph Mapper for Neo4j
I have an independent Symfony bundle (installed with Composer) with entities and repositories to share between my applications that connect same database.
Entities are attached to every applications using configuration (yml shown):
doctrine:
orm:
mappings:
acme:
type: annotation
dir: %kernel.root_dir%/../vendor/acme/entities/src/Entities
prefix: Acme\Entities
alias: Acme
Well, it was the easiest way to include external entities in application, but looks a bit ugly.
Whenever I get repository from entity manager:
$entityManager->getRepository('Acme:User');
I get either preconfigured repository (in entity configuration) or default Doctrine\ORM\EntityRepository.
Now I want to override bundled (or default) repository class for a single entity. Is there any chance to do it with some configuration/extension/etc?
I think, the best looking way is something like:
doctrine:
orm:
....:
Acme\Entities\User:
repositoryClass: My\Super\Repository
Or with tags:
my.super.repository:
class: My\Super\Repository
tags:
- { name: doctrine.custom.repository, entity: Acme\Entities\User }
You can use LoadClassMetadata event:
class LoadClassMetadataSubscriber implements EventSubscriber
{
/**
* #inheritdoc
*/
public function getSubscribedEvents()
{
return [
Events::loadClassMetadata
];
}
/**
* #param LoadClassMetadataEventArgs $eventArgs
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
/**
* #var \Doctrine\ORM\Mapping\ClassMetadata $classMetadata
*/
$classMetadata = $eventArgs->getClassMetadata();
if ($classMetadata->getName() !== 'Acme\Entities\User') {
return;
}
$classMetadata->customRepositoryClassName = 'My\Super\Repository';
}
}
Doctrine Events
Entities are attached to every applications using configuration (yml shown):
Well, it was the easiest way to include external entities in application, but looks a bit ugly.
You can enable auto_mapping
Works for Doctrine versions <2.5
In addition to Artur Vesker answer I've found another way: override global repository_factory.
config.yml:
doctrine:
orm:
repository_factory: new.doctrine.repository_factory
services.yml:
new.doctrine.repository_factory:
class: My\Super\RepositoryFactory
Repository Factory:
namespace My\Super;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
class RepositoryFactory extends DefaultRepositoryFactory
{
/**
* #inheritdoc
*/
protected function createRepository(EntityManagerInterface $entityManager, $entityName)
{
if ($entityName === Acme\Entities\User::class) {
$metadata = $entityManager->getClassMetadata($entityName);
return new ApplicationRepository($entityManager, $metadata);
}
return parent::createRepository($entityManager, $entityName);
}
}
No doubt implementing LoadClassMetadataSubscriber is a better way.
With current symfony 5.3 and doctrine 2.9.5
In your configuration define the service and doctrine.orm.repository_factory:
doctrine:
orm:
#Replace repository factory
repository_factory: 'MyBundle\Factory\RepositoryFactory'
services:
MyBundle\Factory\RepositoryFactory:
arguments: [ '#router', '#translator', '%kernel.secret%' ]
Add you MyBundle/Factory/RepositoryFactory.php file:
<?php declare(strict_types=1);
namespace MyBundle\Factory;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Repository\RepositoryFactory as RepositoryFactoryInterface;
use Doctrine\Persistence\ObjectRepository;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* This factory is used to create default repository objects for entities at runtime.
*/
final class RepositoryFactory implements RepositoryFactoryInterface {
/**
* The list of EntityRepository instances
*
* #var ObjectRepository[]
*/
private $repositoryList = [];
/**
* The kernel secret
*
* #var string
*/
private $secret;
/**
* The RouterInterface instance
*
* #var RouterInterface
*/
private $router;
/**
* The TranslatorInterface instance
*
* #var TranslatorInterface
*/
private $translator;
/**
* Initializes a new RepositoryFactory instance
*
* #param RouterInterface $router The router instance
* #param TranslatorInterface $translator The TranslatorInterface instance
* #param string $secret The kernel secret
*/
public function __construct(RouterInterface $router, TranslatorInterface $translator, string $secret) {
//Set router
$this->router = $router;
//Set secret
$this->secret = $secret;
//Set translator
$this->translator = $translator;
}
/**
* {#inheritdoc}
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository {
//Set repository hash
$repositoryHash = $entityManager->getClassMetadata($entityName)->getName() . spl_object_hash($entityManager);
//With entity repository instance
if (isset($this->repositoryList[$repositoryHash])) {
//Return existing entity repository instance
return $this->repositoryList[$repositoryHash];
}
//Store and return created entity repository instance
return $this->repositoryList[$repositoryHash] = $this->createRepository($entityManager, $entityName);
}
/**
* Create a new repository instance for an entity class
*
* #param EntityManagerInterface $entityManager The EntityManager instance.
* #param string $entityName The name of the entity.
*/
private function createRepository(EntityManagerInterface $entityManager, string $entityName): ObjectRepository {
//Get class metadata
$metadata = $entityManager->getClassMetadata($entityName);
//Get repository class
$repositoryClass = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
//Return repository class instance
//XXX: router, translator and secret arguments will be ignored by default
return new $repositoryClass($entityManager, $metadata, $this->router, $this->translator, $this->secret);
}
}
Then define your MyBundle/Repository/EntityRepository.php:
<?php declare(strict_types=1);
namespace MyBundle\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository as BaseEntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* EntityRepository
*
* {#inheritdoc}
*/
class EntityRepository extends BaseEntityRepository {
/**
* The RouterInterface instance
*
* #var RouterInterface
*/
protected RouterInterface $router;
/**
* The table keys array
*
* #var array
*/
protected array $tableKeys;
/**
* The table values array
*
* #var array
*/
protected array $tableValues;
/**
* The TranslatorInterface instance
*
* #var TranslatorInterface
*/
protected TranslatorInterface $translator;
/**
* The kernel secret
*
* #var string
*/
protected string $secret;
/**
* Initializes a new LocationRepository instance
*
* #param EntityManagerInterface $manager The EntityManagerInterface instance
* #param ClassMetadata $class The ClassMetadata instance
* #param RouterInterface $router The router instance
* #param TranslatorInterface $translator The TranslatorInterface instance
* #param string $secret The kernel secret
*/
public function __construct(EntityManagerInterface $manager, ClassMetadata $class, RouterInterface $router, TranslatorInterface $translator, string $secret) {
//Call parent constructor
parent::__construct($manager, $class);
//Set secret
$this->secret = $secret;
//Set router
$this->router = $router;
//Set slugger
$this->slugger = $slugger;
//Set translator
$this->translator = $translator;
//Get quote strategy
$qs = $manager->getConfiguration()->getQuoteStrategy();
$dp = $manager->getConnection()->getDatabasePlatform();
//Set quoted table names
//XXX: remember to place longer prefix before shorter to avoid strange replacings
$tables = [
'MyBundle:UserGroup' => $qs->getJoinTableName($manager->getClassMetadata('MyBundle:User')->getAssociationMapping('groups'), $manager->getClassMetadata('MyBundle:User'), $dp),
'MyBundle:Group' => $qs->getTableName($manager->getClassMetadata('MyBundle:Group'), $dp),
'MyBundle:User' => $qs->getTableName($manager->getClassMetadata('MyBundle:User'), $dp),
//XXX: Set limit used to workaround mariadb subselect optimization
':limit' => PHP_INT_MAX,
"\t" => '',
"\n" => ' '
];
//Set quoted table name keys
$this->tableKeys = array_keys($tables);
//Set quoted table name values
$this->tableValues = array_values($tables);
}
}
Then simply extend it in MyBundle/Repository/UserRepository.php:
<?php declare(strict_types=1);
namespace MyBundle\Repository;
/**
* UserRepository
*/
class UserRepository extends EntityRepository {
}
I am working on an authentication with FpOpenIdBundle but i get this error
Catchable Fatal Error: Argument 1 passed to Fp\OpenIdBundle\Model\UserIdentity::setUser() must implement interface Symfony\Component\Security\Core\User\UserInterface, instance of Doctrine\ODM\MongoDB\DocumentRepository given, called in C:\xampp\htdocs\project\src\AppBundle\Security\User\OpenIdUserManager.php on line 64 and defined
I followed the doc (https://github.com/formapro/FpOpenIdBundle/blob/master/Resources/doc/configure_user_manager.md)
I have made a manager
namespace AppBundle\Security\User;
use AppBundle\Document\User;
use AppBundle\Document\OpenIdIdentity;
use Doctrine\ODM\MongoDB\DocumentManager;
use Fp\OpenIdBundle\Model\UserManager;
use Fp\OpenIdBundle\Model\IdentityManagerInterface;
class OpenIdUserManager extends UserManager
{
/**
* #var DocumentManager
*/
private $documentManager;
/**
* OpenIdUserManager constructor.
*
* #param IdentityManagerInterface $identityManager
* #param DocumentManager $documentManager
*/
public function __construct(IdentityManagerInterface $identityManager, DocumentManager $documentManager)
{
parent::__construct($identityManager);
$this->documentManager = $documentManager;
}
/**
* #param string $identity
* #param array $attributes
*
* #return \Doctrine\ODM\MongoDB\DocumentRepository
*/
public function createUserFromIdentity($identity, array $attributes = array())
{
$user = $this->documentManager->getRepository('AppBundle:User');
$openIdIdentity = new OpenIdIdentity();
$openIdIdentity->setIdentity($identity);
$openIdIdentity->setAttributes($attributes);
$openIdIdentity->setUser($user);
$this->documentManager->persist($openIdIdentity);
$this->documentManager->flush();
return $user;
}
}
The error is returned because of this line
$openIdIdentity->setUser($user);
I use this manager as the service like this
services:
fp_openid.manager:
class: AppBundle\Security\User\OpenIdUserManager
arguments: [ '#fp_openid.identity_manager', '#doctrine.odm.mongodb.document_manager' ]
Who's called by my security.yml
security:
firewalls:
main:
fp_openid:
create_user_if_not_exists: true
provider: openid_user_manager
providers:
openid_user_manager:
id: fp_openid.manager
main:
entity:
{ class: AppBundle:User, property: personaName }
I finally made a MongoDB document as they said
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Security\Core\User\UserInterface;
use Fp\OpenIdBundle\Document\UserIdentity as BaseUserIdentity;
/**
* #MongoDB\Document(collection="openid_identities")
*/
class OpenIdIdentity extends BaseUserIdentity
{
/**
* #MongoDB\Id(strategy="auto")
*/
protected $id;
/**
* {#inheritdoc}
* #MongoDB\String
*/
protected $identity;
/**
* {#inheritdoc}
* #MongoDB\Hash
*/
protected $attributes;
/**
* #var UserInterface
*
* #MongoDB\ReferenceOne(targetDocument="AppBundle\Document\User", simple=true)
*/
protected $user;
public function __construct()
{
parent::__construct();
}
}
Everythings is working well except this problem, i don't understand why this is not working whereas i do the same as the doc said. I must implement UserInterface instead of instance of DocumentRepository but they use their user document instead.
Does some already use this bundle and had this issue ?
Thanks for helping
Please carefully read the doc again, in the doc it's:
$user = $this->entityManager->getRepository('AcmeDemoBundle:User')->findOneBy(array(
'email' => $attributes['contact/email']
));