2017-10-16 94 views
2

我想通了,回答低于 - 留下问题和所有过程的东西在这里,以便它可以帮助别人找出未来相同的东西,虽然大部分是由答案。ZF2 + Doctrine 2 - 使用工厂创建一个Doctrine SQLFilter - 如何?


我想要做的是,根据本公司,用户与(User#company)相关的过滤器的数据。

我对这个方案的几个实体:

  • 用户
  • 公司
  • 地址

场景为数据,在这种情况下Address实体对象,通过创建User实体。每个User都有一个Company它所属的实体。因此,每个Address都有Address#createdByCompany属性。

现在我试图创建一个SQLFilter扩展,如Doctrine docs - "Working with Filters"所述。

我创建了以下类:

class CreatedByCompanyFilter extends SQLFilter 
{ 
    /** 
    * @var Company 
    */ 
    protected $company; 

    /** 
    * @param ClassMetadata $targetEntity 
    * @param string $targetTableAlias 
    * @return string 
    * @throws TraitNotImplementException 
    */ 
    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) 
    { 
     // Check if Entity implements CreatedByCompanyAwareInterface, if not, return empty string 
     if (!$targetEntity->getReflectionClass()->implementsInterface(CreatedByCompanyInterface::class)) { 

      return ''; 
     } 

     if (!array_key_exists(CreatedByCompanyTrait::class, $targetEntity->getReflectionClass()->getTraits())) { 

      throw new TraitNotImplementException(
       ($targetEntity->getReflectionClass()->getName()) . ' requires "' . CreatedByCompanyTrait::class . '" to be implemented.' 
      ); 
     } 

     return $targetTableAlias . '.created_by_company = ' . $this->getCompany()->getId(); 
    } 

    /** 
    * @return Company 
    */ 
    public function getCompany(): Company // Using PHP 7.1 -> explicit return types 
    { 
     return $this->company; 
    } 

    /** 
    * @param Company $company 
    * @return CreatedByCompanyFilter 
    */ 
    public function setCompany(Company $company): CreatedByCompanyFilter 
    { 
     $this->company = $company; 
     return $this; 
    } 
} 

要使用此过滤器,它注册在配置和安装在模块引导(onBootstrap)被加载。到目前为止,上述情况得到了应用。

但是,上述不通过工厂模式使用。另外,您可能会注意到addFilterConstraint(...)使用了$this->getCompany()->getId(),但$this->setCompany()未被调用,因此创建了一个null值的函数调用。

如何使用Factory来创建此类,或者使用ZF2 ServiceManager的正常路由或通过注册Doctrine本身?


我已经尝试过

下一步Google'ing很多过去几个小时,试图找到一个解决方案,我也试过以下

1 - ZF2工厂

使用在config中注册Factory的默认ZF2方法不起作用:

'service_manager' => [ 
    'factories' => [ 
     CreatedByCompanyFilter::class => CreatedByCompanyFilterFactory::class, 
    ], 
], 

工厂根本不会被调用。这可能与执行顺序有关。我正在考虑Doctrine SQLFilters在ServiceManager完全启动并运行之前得到设置,以防万一我想要做的场景:基于某些基于角色的内容(或“公司内容”)为用户过滤数据这个案例)。

2 - Ocramius's ZF2 Delegator Factories

虽然这方面的工作,我发现Ocramius的委托者工厂。非常有趣的东西,绝对值得一读,并很好地工作。但是,不适合我的场景。我遵循他的指导并创建了一个CreatedByCompanyFilterDelegatorFactory。这是我在配置中注册的,但没有结果,实际的Factory永远不会被调用。

(对不起,已经删除代码)


工厂我试图运行更新为*ListenerFactory,见 '目前试图' 下面

class CreatedByCompanyListenerFactory implements FactoryInterface 
{ 
    // NOTE: REMOVED DOCBLOCKS/COMMENTS FOR LESS CODE 
    public function createService(ServiceLocatorInterface $serviceLocator) 
    { 
     $authService = $serviceLocator->get('zfcuser_auth_service'); 
     $user = $authService->getIdentity(); 

     $listener = new CreatedByCompanyListener(); 
     $listener->setCompany($user->getCompany()); 

     return $listener; 
    } 
} 

当前tr ying

我想我可以尝试从Gedmo Extensions和Doctrine Events剧本中跳出一页,并使用将监听器连接到loadClassMetadata事件的格式。

举个例子,Gedmo的SoftDeleteable有学说的配置中的以下配置,使其工作:

'eventmanager' => [ 
    'orm_default' => [ 
     'subscribers' => [ 
      SoftDeleteableListener::class, 
     ], 
    ], 
], 
'configuration' => [ 
    'orm_default' => [ 
     'filters' => [ 
      'soft-deleteable' => SoftDeleteableFilter::class, 
     ], 
    ], 
], 

所以我想,地狱,让我们来试试,并安装了以下内容:

'eventmanager' => [ 
    'orm_default' => [ 
     'subscribers' => [ 
      CreatedByCompanyListener::class, 
     ], 
    ], 
], 
'configuration' => [ 
    'orm_default' => [ 
     'filters' => [ 
      'created-by-company' => CreatedByCompanyFilter::class, 
     ], 
    ], 
], 
'service_manager' => [ 
    'factories' => [ 
     CreatedByCompanyListener::class => CreatedByCompanyListenerFactory::class, 
    ], 
], 

目的是使用*ListenerFactory获得认证的User实体到*Listener*Listener又将与User关联的Company传递到传递到CreatedByCompanyFilterEventArgs。理论上讲,这个对象应该是可用的。

CreatedByCompanyLister是,就目前而言,以下几点:

class CreatedByCompanyListener implements EventSubscriber 
{ 
    //NOTE: REMOVED DOCBLOCKS/HINTS FOR LESS CODE 
    protected $company; 

    public function getSubscribedEvents() 
    { 
     return [ 
      Events::loadClassMetadata, 
     ]; 
    } 

    public function loadClassMetadata(EventArgs $eventArgs) 
    { 
     $test = $eventArgs; // Debug line, not sure on what to do if this works yet ;) 
    } 

    // $company getter/setter 
} 

不过,我被陷在了*ListenerFactory使用的Zend\Authentication\AuthenticationService,它抛出试图让User与身份时,一个异常$user = $authService->getIdentity();

内部此功能延续到ZfcUser\Authentication\Storage\Db类,线70,($identity = $this->getMapper()->findById($identity);),它崩溃,投掷getMapper()

Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for doctrine.entitymanager.orm_default

步进了(在PhpStorm的XDebug)调用不知何故让它继续一点点(跆拳道的位...),但随后抛出:

An exception was raised while creating "zfcuser_user_mapper"; no instance returned

以 “*异常*以前”,上面写着:

Circular dependency for LazyServiceLoader was found for instance Doctrine\ORM\EntityManager

独立我知道所有的这些错误的意思,但我从来没有见过所有的他们被相同的函数调用抛出(->getMapper())b安伏。有任何想法吗?

回答

0

我当然认为wayyyyyy太复杂了。现在我知道了运行使用以下类:

  • CreatedByCompanyFilter
  • CreatedByCompanyListener
  • CreatedByCompanyListenerFactory

唯一配置,我需要的是以下几点:

'listeners' => [ 
    CreatedByCompanyListener::class, 
], 
'service_manager' => [ 
    'factories' => [ 
     CreatedByCompanyListener::class => CreatedByCompanyListenerFactory::class, 
    ], 
], 

注意:未在Doctrine配置中注册过滤器。所以不要在下面做,如果你想这样做!

'doctrine' => [ 
    'eventmanager' => [ 
     'orm_default' => [ 
      'subscribers' => [ 
       CreatedByCompanyListener::class, 
      ], 
     ], 
    ], 
    'configuration' => [ 
     'orm_default' => [ 
      'filters' => [ 
       'created-by-company' => CreatedByCompanyFilter::class, 
      ], 
     ], 
    ], 
], 

重复:上述不需要在这种情况下 - 虽然看到我已经提出来,我可以看到我(和其他人)怎么可能认为它可能是。


所以,仅仅3个班,其中一个的在ServiceManager配置注册Listener,注册ListenerFactory

逻辑的结果是Listener需要启用FilterDoctrine EntityManager,之后可以在返回值(过滤器)中设置所需的参数。不知道“Daredevel”是否有一个Stackoverflow帐户,但thanks for this article!其中我注意到启用过滤器,然后设置其参数。

因此,Listener启用Filter并设置其参数。

CreatedByCompanyListener如下:

class CreatedByCompanyListener implements ListenerAggregateInterface 
{ 
    // NOTE: Removed code comments & docblocks for less code. Added inline for clarity. 

    protected $company;   // Type Company entity 
    protected $filter;   // Type CreatedByCompanyFilter 
    protected $entityManager; // Type EntityManager|ObjectManager 
    protected $listeners = []; // Type array 

    public function attach(EventManagerInterface $events) 
    { 
     $this->listeners[] = $events->attach(MvcEvent::EVENT_BOOTSTRAP, [$this, 'onBootstrap'], -5000); 
    } 

    public function detach(EventManagerInterface $events) 
    { 
     foreach ($this->listeners as $index => $listener) { 
      if ($events->detach($listener)) { 
       unset($this->listeners[$index]); 
      } 
     } 
    } 

    public function onBootstrap(MvcEvent $event) 
    { 
     $this->getEntityManager()->getConfiguration()->addFilter('created-by-company', get_class($this->getFilter())); 
     $filter = $this->getEntityManager()->getFilters()->enable('created-by-company'); 

     $filter->setParameter('company', $this->getCompany()->getId()); 
    } 

    // Getters & Setters for $company, $filter, $entityManager 
} 

CreatedByCompanyListenerFactory如下:

类CreatedByCompanyListenerFactory实现FactoryInterface { 公共功能的CreateService(ServiceLocatorInterface $的ServiceLocator) { $ authService = $的ServiceLocator - >获取( 'zfcuser_auth_service'); $ entityManager = $ serviceLocator-> get(EntityManager :: class); $ listener = new CreatedByCompanyListener();

$user = $authService->getIdentity(); 
    if ($user instanceof User) { 
     $company = $user->getCompany(); 
    } else { 
     // Check that the database has been created (more than 0 tables) 
     if (count($entityManager->getConnection()->getSchemaManager()->listTables()) > 0) { 
      $companyRepo = $entityManager->getRepository(Company::class); 
      $company = $companyRepo->findOneBy(['name' => 'Guest']); 
     } else { 
      // Set temporary company for guest user 
      $company = $this->tmpGuestCompany(); // Creates "new" Company, sets name, excludes in $entityManager from persisting/flushing it 
     } 
    } 

    $listener->setCompany($company); 
    $listener->setFilter(new CreatedByCompanyFilter($entityManager)); 
    $listener->setEntityManager($entityManager); 

    return $listener; 
} 

}

最后,CreatedByCompanyFilter需要真正做一些事情,所以它如下:

class CreatedByCompanyFilter extends SQLFilter 
{ 
    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) 
    { 
     // Check if Entity implements CreatedByCompanyAwareInterface, if not, return empty string 
     if (!$targetEntity->getReflectionClass()->implementsInterface(CreatedByCompanyInterface::class)) { 

      return ''; 
     } 

     if (!array_key_exists(CreatedByCompanyTrait::class, $targetEntity->getReflectionClass()->getTraits())) { 

      throw new TraitNotImplementedException(
       $targetEntity->getReflectionClass()->getName() . ' requires "' . CreatedByCompanyTrait::class . '" to be implemented.' 
      ); 
     } 

     $column = 'created_by_company'; 
     return "{$targetTableAlias}.{$column} = {$this->getParameter('company')}"; 
    } 
} 

为什么这个工程

最性重要NT位出上面的代码如下,基于Daredevel的教程前面链接:

public function onBootstrap(MvcEvent $event) 
{ 
    $this->getEntityManager()->getConfiguration()->addFilter('created-by-company', get_class($this->getFilter())); 
    $filter = $this->getEntityManager()->getFilters()->enable('created-by-company'); 

    $filter->setParameter('company', $this->getCompany()->getId()); 
} 

这是我们第一行添加FilterEntityManager的配置。这使我们能够使用它。要求您给Filter一个名称,并告诉EntityManager哪个类属于第二个参数的名称。

接下来,我们enable()Filter的名称。 Daredevel的教程告诉我,enable()函数实际上返回刚启用的过滤器。

使用$filter变量中返回的Filter,我们现在可以使用该实例来设置参数,我们将在上一条语句中进行设置。


这是怎么从我在我的问题试过有什么不同?嗯,在我的问题中,我试图以相反的方式去做。我试图通过配置和Listener启用Filter。但是,对于后一种方法,我试图在变量中创建并存储一个Filter实例,设置所需的参数,然后在EntityManager中启用它,这恰恰是错误的方法,因为enabling创建了一个新实例(因此没有变量)。

因此,我将此留给任何可能会偶然发现同一问题的人。