PHP je věda

Objektově orientované programování nejen v PHP

Template objekt propojuje view a controller

Jak bylo dříve napsáno, controller kontroluje view. Controller tedy musí vědět, jaký view při jaké url načíst. Jsou frameworky, které to dělají automaticky, nebo je někdy možné explicitně v controlleru view nastavit. Jakým způsobem se o správné šabloně rozhodne není až tak důležité, důležitější je na tomto místě jak vložit data do template objektu. V klasickém přípdadě, když je business logic zapsána v kontolleru, to nebývá problém. Klasicky se podle výsledků zapisuje do template objektu a tento se na konci action vrátí. Template Engine se potom postará o to, aby byl template object správně předán do view.

<?php
class Controller {
 
    public function voteAction() {
 
        $userId = $_SESSION['user_id'];
        $votedProjectId = $_REQUEST['voted_project_id'];
        $tmplObject = array();
 
        $project = $this->projectRepostitory->findById($votedProjectId);
        $user = $this->userRepository->findById($userId);
 
        if ($project->hasVoteFrom($user)) {
            $project->voteByUser($userId);
            $tmplObject["voteSucceed"] = true;
        } else {
            $tmplObject["voteSucceed"] = false;
        }
        $tmplObject['newVotesCount'] = $project->getVotesCount();
        return $tmplObject;
    }
 
 
}
 

V kapitole o controlleru bylo však zdůrazňováno, že kód v controlleru není znovu použitelný. I když pro tento jednoduchý příklad je tento kód opravdu to nejlepší řešení, je tento příklad vhodný pro ukázku jak veškerou business logiku zapouzdřit jako službu a přitom získat veškeré informace, které jsou pro view nutné.

Nejprve je potřeba si vytvořit službu – tedy model hlasování – pomocí které bude možné hlasovat a definovat interface, pomocí kterého může okolí se službou komunikovat. Definice interface je nutná, jelikož od metody vote očekáváme (např. v úplně jiné části programu), že její návratová hodnota bude true nebo false, nikoliv template data.

Předpokládejme, že projectRepository a userRepository byly deklarovány v konstruktoru třídy VotingService.

<?php
 
interface VotingServiceTplInterface {
 
    /**
     * @param bool $succeed
     */
    public function setVoteSucceed($succeed);
 
    /**
     * @param int $votesCount
     */
    public function setNewVotesCount($votesCount);
}
 
class VotingService {
 
 
    /**
     * @param                           $userId
     * @param                           $projectId
     * @param VotingServiceTplInterface $templateObject
     * @return bool
     */
    public function vote($userId, $projectId, VotingServiceTplInterface $templateObject) {
        $project = $this->projectRepostitory->findById($projectId);
        $user    = $this->userRepository->findById($userId);
 
        if ($project->hasVoteFrom($user)) {
            $project->voteByUser($userId);
            $templateObject->setVoteSucceed(true);
            $tmplObject["voteSucceed"] = true;
            return false;
 
        } else {
            $templateObject->setVoteSucceed(false);
        }
        $templateObject->setNewVotesCount($project->getVotesCount());
 
        return true;
    }
}
 

Pokud existuje takováto služba a my ji chceme použít v našem controlleru a také chceme použít data pro view, je potřeba vytvořit třídu, která bude implementovat VotingServiceTmplInterface a o vykonání business logiky se postará třída VotingService.

<?php
 
class TemplateObject implements VotingServiceTplInterface {
 
    protected $data = array();
 
    public function setVoteSucceed($succeed)
    {
        $this->data["voteSucceed"] = $succeed;
    }
 
    public function setNewVotesCount($votesCount)
    {
        $this->data["votesCount"] = $votesCount;
    }
 
    public function getTemplateData() {
        return $this->data;
    }
}
 
class ControllerWithService {
 
    public function voteAction() {
 
        $userId = $_SESSION['user_id'];
        $votedProjectId = $_REQUEST['voted_project_id'];
        $templateObject = new TemplateObject();
 
        $votingService = new VotingService();
        $votingService->vote($userId, $votedProjectId, $templateObject);
 
        return $templateObject;
    }
 
 
}
 
 

Interface je pro některé programátory novinkou. Zpravidla se s tímto nástrojem programátor nesetká a u jednoduchých programů, kterou píše programátor v controlleru ani interface zpravidla není potřeba. Výhoda použití interface je v tom, že metoda VotingService::vote definuje, jaká data může během svých výpočtů dodat. Při implementaci interface má tedy programátor možnost se soustředit pouze na to, jakým způsobem přijatá data zpracuje. Při implementaci kontrolleru se potom musí zabývat jen tím, jak poskytnout metodě VotingService::vote všecha potřebná data a objekty, které služba potřebuje, aby mohla výpočet udělat.

Využití interface pro odpovědi a zapouzdření logiky do znovupoužitelných služeb dělá náš kód nezávislý na použitém frameworku. Je jasné, že jednou napsanou metodu je možné zavolat odkudkoliv. Jak nám ale zajišťuje nezávislost na frameworku interface? Interface nám v tomto případě zajišťuje nezávislost na prezentační vrstvě. Prezentační vrstvu je možné libovolně zaměnit. Pokud bychom se rozhodli, že přejdeme z vlastního template enginu na smarty, je potřeba pouze znovu implementovat interface a v controlleru vytvoříme odpovídající instanci Template Interface.

<?php
 
class SmartyTemplateObject implements VotingServiceTplInterface {
 
    /**
     * @var array
     */
    protected $data = array();
 
    /**
     * @var Smarty
     */
    protected $smarty;
 
    public function __construct(Smarty $smarty) {
        $this->smarty = $smarty;
    }
 
    public function setVoteSucceed($succeed)
    {
        $this->smarty->assign("voteSucceed", $succeed);
    }
 
    public function setNewVotesCount($votesCount)
    {
        $this->smarty->assign("votesCount", $votesCount);
    }
}
 
class ControllerWithSmarty {
 
    public function voteAction() {
 
        $userId = $_SESSION['user_id'];
        $votedProjectId = $_REQUEST['voted_project_id'];
        $smarty = new Smarty();
        $templateObject = new SmartyTemplateObject($smarty);
 
        $votingService = new VotingService();
        $votingService->vote($userId, $votedProjectId, $templateObject);
 
        return $smarty;
    }
 
 
}
 
 

Další výhodou takovéhoto stylu programování je to, že „frontend vývojář“ vidí na první pohled v implemetaci TemplateInterface, jaká data vůbec může v template objektu očekávat a že business logika je naprosto nezávislá od dat. Možná by se dalo říci, že tyto Template Interfaces jsou vlastně modelem ve smyslu MVC.