DoctrineService und ein BaseEntity

Heute habe ich mir eine Abstrakte Klasse geschaffen für Services geschaffen. Ich habe hierbei alle Funktionalitäten abgebildet die man für die Basis Operationen mit den Entities braucht. Hiermit kann nun einfach aus der Datenbank ein und ausgelesen werden, weiters bietet diese Klasse einen guten Startpunkt für weitere Buisnesslogik und sollte auch als zentraler Zugangspunkt, für den Controller gesehen werden um mit der Datenbank zu kommunizieren. Weiters habe ich in Kombination mit einen einheitlichen Basis Entity die möglichkeit geschaffen die Entities einfach in Arrays umzuwandeln, dies ist vor allem für die Restbasierten Controller gedacht, da diese ja z.B. über JSON Kommunizieren und so lediglich das Array zurückgegeben werden kann ohne sich um die generierung zu kümmern. Angedacht ist dann auch noch den Controller so zu erweitern das er die Basisvalidierung von allen Entities fast fertig vor implementiert und nur mehr spezielle Ausprägungen aus implementiert werden müssen.

Nun aber zum Code, hier erst mal das Service:


<?php
namespace Vico\Service;

use Doctrine\ORM\EntityManager;
use Vico\Entity\BaseEntity;

/**
 * User: Zelle
 * Date: 05.01.13
 * Time: 14:21
 *
 */ 
abstract class DoctrineReadyService{

    /**
     * Returns the name of the class of the entity
     * (represents the name of the entity)
     * should be overwritten in all inheritated classes
     * @return string
     */
    protected function getMainEntityName(){
        return null;
    }

    /**
     *
     * @var Doctrine\ORM\EntityManager
     */
    protected $entityManager;

    public function __construct(EntityManager $em) {
        $this->entityManager = $em;
    }

    /**
     * Sets the EntityManager
     *
     * @param EntityManager $em
     * @access protected
     * @return DoctrineReadyService
     */
    protected function setEntityManager(EntityManager $em)
    {
        $this->entityManager = $em;
        return $this;
    }

    /**
     * Returns the EntityManager
     *
     * Fetches the EntityManager from ServiceLocator if it has not been initiated
     * and then returns it
     *
     * @access protected
     * @return EntityManager
     */
    protected function getEntityManager()
    {
        return $this->entityManager;
    }

    /**
     * Search for an entity with the unique identifier
     * @param $id
     * @return BaseEntity
     */
    public function findById($id){
        $this->checkRepository();
        return $this->getMainRepository()->find($id);
    }

    /**
     * search for an entity with the unique identifier and creates an array out of it
     * @param $id
     * @return array|null
     */
    public function findByIdAsArray($id){
        $entity = $this->findById($id);
        if($entity != null){
            return $this->entityToArray($entity);
        }else{
            return null;
        }
    }

    /**
     * Returns all entities in the database
     * @return BaseEntity[]
     */
    public function findAll(){
        $this->checkRepository();
        return $this->getMainRepository()->findAll();
    }

    /**
    * Returns all entities in the database in form of an array
    * @return BaseEntity[]
    */
    public function findAllAsArray(){
        $this->checkRepository();
        return $this->entitiesToArray($this->getMainRepository()->findAll());
    }

    /**
     * @param $entity
     * @return BaseEntity
     * @throws \Exception
     */
    public function save($entity){
        if(!is_a($entity, $this->getMainEntityName())){
            throw new \Exception("Unexcpected entity class ".$this->getMainEntityName()." ".__CLASS__." ".__METHOD__);
        }
        $this->getEntityManager()->persist($entity);
        $this->getEntityManager()->flush();
        return $entity;
    }

    /**
     * @param BaseEntity $entity
     * @return array
     * @throws \Exception
     */
    private function entityToArray($entity){
        if(!is_a($entity, $this->getMainEntityName())){
            throw new \Exception("Unexcpected entity class ".$this->getMainEntityName()." ".__CLASS__." ".__METHOD__);
        }
        return $entity->toArray();
    }

    /**
     * @param BaseEntity[] $entities
     * @return array
     */
    private function entitiesToArray(array $entities){
        $array = array();
        foreach($entities as $entity){
            $array[] = $this->entityToArray($entity);
        }
        return $array;
    }

    /**
     * @throws Exception
     */
    private function checkRepository(){
        if($this->getMainEntityName() === null){
            throw new \Exception("Entity Name must be set is (".$this->getMainEntityName().") instead");
        }
    }

    /**
     * @return EntityRepository
     */
    private function getMainRepository(){
        return $this->getEntityManager()->getRepository($this->getMainEntityName());
    }

}

Es muss prinzipiell nur getMainEntityName überschrieben werden, damit das Service ordnungsgemäß funktionieren kann. In dieser Methode wird der Name des Entities festgelegt, mit welchen gearbeitet wird.
Ist dieser in der erbenden Klasse nicht gesetzt, so wird eine Exception geworfen um zu verhindern das nicht laufende Services instanziert bzw. genutzt werden. Außerdem wird bei der Methode save überprüft ob das Entity wirklich zu den Service gehört. Prinzipiell könnte zwar jedes Entity gespeichert werden, jedoch sollen die Service explizit nur für das definierte Entity nutzbar sein um so die Rollen für die Datenbankzugriffe klar zu trennen.
Gelöst wurde das hier über eine nicht abstracte Methode die null zurückliefert, ich wollte zwar auch diese abstract machen um so die Erbendeklasse zu zwingen, diese Methode zu überschreiben, jedoch kann dann in der abstrakten Klasse nicht mehr auf dieses Feld zugegriffen werden, deshalb also diese Lösungsvariante.
Das BaseEntity bietet grundsätzlich 2 Funktionalitäten an: eine Methode um ein Array aus dem Entity zu erstellen und eine Methode um aus einen Array ein Entity zu befüllen, wobei lediglich geprüft wird ob das Feld im Array existiert, wenn nicht wird es mit null befüllt. Das Validieren und Filtern sollte hier bereits nicht mehr nötig sein bzw. bereits vorher geschehen.

abstract class BaseEntity {

    /**
     * creates an array based on all fields of the entity
     * @return array
     */
    abstract public function toArray();

    /**
     * sets all fields with data out of the array, there is no checking off violations necessary
     * @param array $data
     * @return void
     */
    abstract public function fillWithArray(array $data);
}

Ein konkretes Service kann z.B. so dazu aussehn


class InstitutionService extends DoctrineReadyService {
    /**
     * Returns the name of the class of the entity
     * (represents the name of the entity)
     * @return string
     */
    protected function getMainEntityName()
    {
        return 'Vico\Entity\Institution';
    }

}

Wie man sieht muss nur mehr der Name zurückgegeben werden und alles sollte funktionieren.

Der nächste Schritt ist nun den Controller dahingehend zu abstrahieren, das nur mehr spezielle Methoden implementiert werden müssen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Time limit is exhausted. Please reload CAPTCHA.

Anti-Spam durch WP-SpamShield

Scroll to top