2013-04-26 2 views
0

У меня есть немного вопрос OOD.Zend Framework 2 - Использовать метод обслуживания как параметр InputFilter

У меня есть обслуживание:

namespace Front\Service\Course; 

use Front\ORM\EntityManagerAwareInterface; 
use Zend\Http\Request; 
use Zend\InputFilter\InputFilter; 
use Front\InputFilter\Course\CreateFilter; 

class Create implements EntityManagerAwareInterface 
{ 
/** 
* @var \Doctrine\Orm\EntityManager 
*/ 
protected $entityManager = null; 

public function create(CreateFilter $createFilter) 
{ 
    if (!$createFilter->isValid()) return false; 

    /* @var $courseRepository \Front\Repositories\CourseRepository */ 
    $courseRepository = $this->getEntityManager()->getRepository('Front\Entities\Course'); 
    $course = $courseRepository->findByName($createFilter->getCourse()); 
} 

/* (non-PHPdoc) 
* @see \Front\ORM\EntityManagerAwareInterface::getEntityManager() 
*/ 
public function getEntityManager() 
{ 
    return $this->entityManager; 
} 

/* (non-PHPdoc) 
* @see \Front\ORM\EntityManagerAwareInterface::setEntityManager() 
*/ 
public function setEntityManager(\Doctrine\ORM\EntityManager $entityManager) 
{ 
    $this->entityManager = $entityManager; 
    return $this; 
} 
} 

И контроллер:

class CreateController extends \Zend\Mvc\Controller\AbstractController 
{ 

    public function onDispatch(MvcEvent $e) 
    { 
     $jsonModel = new JsonModel(); 

     /* @var $courseCreateService \Front\Service\Course\Create */ 
     $courseCreateService = $this->getServiceLocator()->get('Front\Service\Course\Create'); 

     $courseCreateFilter = new CreateFilter(); 

     $courseCreateFilter->setData($this->params()->fromPost()); 

     if (!$courseCreateFilter->isValid()) { 
      $jsonModel->setVariable('status', 0); 
      $jsonModel->setVariable('message', $courseCreateFilter->getMessages()); 

      return; 
     } 

     $courseCreateService->create($courseCreateFilter); 

     } 
} 

метод обслуживание декларация:

public function create(CreateFilter $createFilter) 

я заставить пользователь Службы использовать CreateFilter контейнер, который, полученные из Zend/InputFilter каждый раз, когда он хочет создать новый курс.

Мой вопрос: может быть, лучше, когда я отправлю на сервисный слой, а не на типизированный объект, но простое значение? На примере в моем случае это мощь выглядит следующим образом:

public function create($courseName) 

Мой CreateFilter выглядит следующим образом:

class CreateFilter extends InputFilter 
{ 
    public function __construct() 
    { 
     $input = new Input('name'); 

    $validatorChain = new ValidatorChain(); 

    $validatorChain->addValidator(new StringLength(array('max'=>60))) 
        ->addValidator(new NotEmpty()); 

    $input->setRequired(true)->setValidatorChain($validatorChain); 

    $this->add($input); 
} 

/** 
* @return string | null 
*/ 
public function getCourse() 
{ 
    return $this->getValue('name'); 
} 
} 
+0

Если вы ожидаете, что это будет входной фильтр, ваш класс должен отразить это, но вместо того, чтобы форсировать реализацию конкретного класса, почему бы просто не потребовать, чтобы предоставленный класс реализовал «Zend \ InputFilter \ InputFilterInterface»? – Crisp

+0

Во-первых, это не моя зависимость, это пользовательский ввод с точки зрения сервиса. И он должен иметь конкретный метод getCourse, определенный внутри CreateFilter. – SpalaX

+0

Если служба ожидает, что у нее будет метод getCourse, тогда ваш класс фильтра должен реализовать интерфейс, который определяет это. Моя точка зрения, ваша служба должна ожидать, что объект, реализующий этот интерфейс вместо конкретного имени класса, вместо того, чтобы быть беспричинным. – Crisp

ответ

0

Если вы предоставите конкретное имя класса, как вы делаете сейчас, вы навсегда связаны к конкретной реализации класса или одного, полученного из него. Если позднее вы решите, что хотите полностью использовать другой класс, вам придется реорганизовать код класса обслуживания, тогда как с интерфейсом вам нужно будет реализовать его только в новом классе, и ваша служба будет продолжать работать без каких-либо изменений.

Без какого-либо интерфейса ваш класс обслуживания должен будет выполнить дополнительные проверки, чтобы сначала увидеть, является ли он объектом, а затем, если он реализует метод, который вы ожидаете, прежде чем он даже начнет выполнять свою работу. Требуя интерфейса, вы устраняете неопределенность и отрицаете необходимость проверок.

Предоставляя интерфейс, вы создаете контракт между вашими методами и классами, которые они ожидают как аргументы, не ограничивая, какие классы могут заключать контракт. В общем, контракт по интерфейсу предпочтительнее заключить контракт по имени класса, но оба они вообще не имеют контракта.

+0

Если я определяю интерфейс, как я могу быть уверен, что данный объект реализует деловое поведение CreateFilter? – SpalaX

+0

Класс может реализовать столько интерфейсов, сколько вам нужно, если другие методы вашего сервиса имеют другие требования, объявить, какой интерфейс подходит для сигнатур метода. – Crisp

0

Обычно я связываю свои объекты с моей формой, поэтому они заполняются данными из формы. Таким образом, вы вводите объект в свою службу, а imho - намного чище. Служба не должна знать, как вы получили данные.

Мой контроллер «админ» для сущности. В строке обычно вводятся три объекта: репозиторий (для запросов объектов), служба (для сохранения/обновления/удаления объектов) и формы (для изменения объектов для пользователя) , Стандартный контроллер затем очень CR на основе и только толкает объекты на уровень услуг:

<?php 

namespace Foo\Controller; 

use Foo\Repository\Bar as Repository; 
use Foo\Form\Bar  as Form; 
use Foo\Service\Bar as Service; 
use Foo\Entity\Bar  as Entity; 
use Foo\Options\ModuleOptions; 

use Zend\Mvc\Controller\AbstractActionController; 

class BarController extends AbstractActionController 
{ 
    /** 
    * @var Repository 
    */ 
    protected $repository; 

    /** 
    * @var Service 
    */ 
    protected $service; 

    /** 
    * @var Form 
    */ 
    protected $form; 

    /** 
    * @var ModuleOptions 
    */ 
    protected $options; 

    public function __construct(Repository $repository, Service $service, Form $form, ModuleOptions $options = null) 
    { 
     $this->repository = $repository; 
     $this->service = $service; 
     $this->form  = $form; 

     if (null !== $options) { 
      $this->options = $options; 
     } 
    } 

    public function getService() 
    { 
     return $this->service; 
    } 

    public function getRepository() 
    { 
     return $this->repository; 
    } 

    public function getForm() 
    { 
     return $this->form; 
    } 

    public function getOptions() 
    { 
     if (null === $this->options) { 
      $this->options = new ModuleOptions; 
     } 

     return $this->options; 
    } 

    public function indexAction() 
    { 
     $bars = $this->getRepository()->findAll(); 

     return array(
      'bars'  => $bars, 
     ); 
    } 

    public function viewAction() 
    { 
     $bar = $this->getBar(); 

     return array(
      'bar' => $bar, 
     ); 
    } 

    public function createAction() 
    { 
     $bar = $this->getBar(true); 
     $form = $this->getForm(); 
     $form->bind($bar); 

     if ($this->getRequest()->isPost()) { 
      $data = $this->getRequest()->getPost(); 
      $form->setData($data); 

      if ($form->isValid()) { 
       // Bar is populated with form data 
       $this->getService()->create($bar); 

       return $this->redirect()->toRoute('bar/view', array(
        'bar' => $bar->getId(), 
       )); 
      } 
     } 

     return array(
      'form' => $form, 
     ); 
    } 

    public function updateAction() 
    { 
     $bar = $this->getBar(); 
     $form = $this->getForm(); 
     $form->bind($bar); 

     if ($this->getRequest()->isPost()) { 
      $data = $this->getRequest()->getPost(); 
      $form->setData($data); 

      if ($form->isValid()) { 
       $this->getService()->update($bar); 

       return $this->redirect()->toRoute('bar/view', array(
        'bar' => $bar->getId(), 
       )); 
      } 
     } 

     return array(
      'bar' => $bar, 
      'form' => $form, 
     ); 
    } 

    public function deleteAction() 
    { 
     if (!$this->getRequest()->isPost()) { 
      $this->getRequest()->setStatusCode(404); 
      return; 
     } 

     $bar = $this->getBar(); 
     $this->getService()->delete($bar); 

     return $this->redirect()->toRoute('bar'); 
    } 

    protected function getBar($create = false) 
    { 
     if (true === $create) { 
      $bar = new Entity; 
      return $bar; 
     } 

     $id = $this->params('bar'); 
     $bar = $this->getRepository()->find($id); 

     if (null === $bar) { 
      throw new Exception\BarNotFoundException(sprintf(
       'Bar with id "%s" not found', $id 
      )); 
     } 

     return $bar; 
    } 
} 

Я сделал GIST файл на Github с этим полным кодом (лучше читаемые) и обслуживание. Сервис опирается на интерфейс, поэтому вы можете даже поменять объект объекта другим, имеющим тот же интерфейс.

Проверить полную вещь здесь: https://gist.github.com/juriansluiman/5472787

+0

Пример выше и по ссылке, в контроллере и обслуживании прерывает SRP (принцип единой ответственности). В конструкторе контроллера ломается класс DIP (принцип инверсии зависимостей) зависит от конкретного класса (объект формы), но должен зависеть от абстракции с помощью DIP. Но в любом случае спасибо, вы дали мне еще одну часть головоломки =) – SpalaX

+0

На вопрос, который вы задаете (должен ли я вводить InputFilter в свой сервисный уровень), мой пример - я думаю - проще всего понять. Обычно я полагаюсь на интерфейсы, но определение интерфейсов в SO-ответе создает просто массивный беспорядок. Можете ли вы объяснить, почему этот контроллер не обрабатывает SRP? И то же самое для службы? Насколько я вижу, там нет нарушения;) –

+0

Чтобы прояснить больше: на ваш вопрос: должен ли я создавать «типизированный» или просто значение. Мой ответ: ** никогда ** не вводите фильтр ввода, но ваш объект (и там, интерфейс лучше, чем конкретный экземпляр). Также ** никогда ** не делайте свою службу 'EntityManagerAwareInterface', так как это действительно * неправильный * путь DIC с инициализаторами для жестких зависимостей. Также ** никогда не создавайте контроллеры на базе * action *, а на базе * entity *: не используйте 'CreateController', а' CourseController'. Я думаю, что это более фундаментальные части кодирования, на которые я хотел ответить/помочь. –

0

Спасибо всем за ответы, из-за ответы и анализа, я достиг вывод, который наиболее применимый для моей ситуации. Я согласен, что Сервис в моем случае не должен ждать конкретного объекта, он должен подождать абстракции с помощью метода getCourse.
И я полностью согласен с «хрустящим» ответом:

В целом, контракт на интерфейсе предпочтительнее сжиматься по имени класса, но оба предпочтительнее без контракта вообще.

Поэтому мне нужно, чтобы извлечь интерфейс с одним методом getCourse или GetName и удалить

if (!$createFilter->isValid()) return false; 

так Интерфейс:

interface CourseInterface 
{ 
    /** 
    * @return String 
    **/ 
    public function getName(); 
} 

и обслуживание:

class Create implements EntityManagerAwareInterface 
{ 
/** 
* @var \Doctrine\Orm\EntityManager 
*/ 
protected $entityManager = null; 

/** 
* @param CourseInterface $course 
* @param UserInterface $creator 
*/ 
public function create(CourseInterface $course) 
{ 
    $courseEntity = new Course(); 

    $courseEntity->setName($course->getName()); 

    $this->entityManager->persist($courseEntity); 
    $this->entityManager->flush(); 
..... 

Спасибо всем.

+0

Нет, теперь вы вводите «CourseInterface» и создаете «CourseEntity». Вы рассматриваете реализацию интерфейса как объект значения, где вы скорее должны сделать свой объект реализованным интерфейсом. Да, полагаться на интерфейс предпочтительнее, чем полагаться на конкретный пример, но теперь вы смешиваете все вместе. Не обрабатывать объект как объект ценности. Они сохраняют состояние, не используют объекты значения для изменения вашего состояния объектов, если только вы не работаете с объектной моделью без сеттеров (http://whitewashing.de/2012/08/22/building_an_object_model__no_setters_allowed.html) –

+0

CourseInterface it является частью пространства имен данных, $ course является объектом данных/значений, он не является сущностью. Но спасибо за вашу заботу. – SpalaX

Смежные вопросы