Pull to refresh

Обмен опытом. Где реализовать знание об адресах контроллера?

Reading time 4 min
Views 2.9K

Постановка проблемы


Необходимо определить знание об адресе контроллера в одном слое системы. Это позволит быстро осуществлять поиск и безболезненно производить рефакторинг контроллеров и их адресов.

UPD: Для того чтобы показать проблему более наглядно, можно привести следующий пример, когда адреса контроллеров задаются в системе где только угодно, в контроллерах, в представлениях, иногда даже в клиентских скриптах, и этот процесс становится неуправляемым.

Реализовать проверку достаточности параметров для построения адреса. Это необходимо если параметры для адреса задаются в другом слое системы, например в представлении или клиентском скрипте.

Решение


Все адреса должны быть определены в контроллерах. При необходимости недостающие параметры можно заполнить в слое представления или клиентского скрипта.

Для удобной работы можно определить помощника — построитель адресов.



Исходный код построителя адреса
use CUrlManager;

class UrlBuilder {
    const PARAMETER_NAME_HASH = '#';

    /** @var CUrlManager */
    private $urlManager;
    /** @var string */
    private $route;
    /** @var array */
    private $params = array();
    /** @var array */
    private $required = array();

    public static function className() {
        return get_called_class();
    }

    /**
     * @param CUrlManager $urlManager
     */
    public function __construct(CUrlManager $urlManager) {
        $this->urlManager = $urlManager;
    }

    /**
     * @return CUrlManager
     */
    public function getUrlManager() {
        return $this->urlManager;
    }

    /**
     * @return string
     */
    public function getRoute() {
        return $this->route;
    }

    /**
     * @param string $route
     * @return $this
     */
    public function setRoute($route) {
        $this->route = $route;
        return $this;
    }

    /**
     * @return array
     */
    public function getParams() {
        return $this->params;
    }

    /**
     * @param array $params
     * @return $this
     */
    public function setParams($params) {
        $this->params = $params;
        return $this;
    }

    /**
     * @param string $name
     * @return mixed
     * @throws Exception
     */
    public function getParam($name) {
        if (!array_key_exists($name, $this->params)) {
            throw new Exception(sprintf('This param `%s` not exists'));
        }
        return $this->params[$name];
    }

    /**
     * @param string $name
     * @param mixed $value
     * @return $this
     */
    public function setParam($name, $value) {
        $this->params[$name] = $value;
        return $this;
    }

    /**
     * @return string
     * @throws Exception
     */
    public function getHash() {
        return $this->getParam(self::PARAMETER_NAME_HASH);
    }

    /**
     * @param string $value
     * @return $this
     */
    public function setHash($value) {
        $this->setParam(self::PARAMETER_NAME_HASH, $value);
        return $this;
    }

    /**
     * @return array
     */
    public function getRequired() {
        return $this->required;
    }

    /**
     * @param array $required
     * @return $this
     */
    public function setRequired($required) {
        $this->required = $required;
        return $this;
    }

    /**
     * @throws Exception
     * @return string
     */
    public function getUrl() {
        if ($this->hasRequiredParams()) {
            throw new Exception(sprintf('Required params `%s` not exists', implode(', ', $this->requiredParams())));
        }
        return $this->getUrlManager()->createUrl($this->route, $this->params);
    }

    /**
     * @return array
     */
    public function toArray() {
        return array(
            'route' => $this->route,
            'params' => $this->params,
            'required' => $this->required,
        );
    }

    /**
     * @return UrlBuilder
     */
    public function copy() {
        return clone $this;
    }

    protected function hasRequiredParams() {
        return (boolean)$this->requiredParams();
    }

    protected function requiredParams() {
        return array_diff($this->required, array_keys(array_filter($this->params)));
    }
}



Примеры использования


Определение знания об адресе в контроллере

Базовый абстрактный контроллер. Реализация метода создания обектов построителя адреса
class BaseController extends \CController {

    public function createUrlBuilder($route, $params = array()) {
        $urlBuilder = new UrlBuilder($this->getUrlManager());
        $urlBuilder
            ->setRoute($route)
            ->setParams($params);
        return $urlBuilder;
    }

    public function getUrlManager() {
        $urlManager = $this->getApp()->getUrlManager();
        return $urlManager;
    }

    public function getApp() {
        return \Yii::app();
    }
}




Конкретный контроллер. Использование построителя адреса
class SiteController extends BaseController {

    public function actionIndex() {
        return $this->render('index', array(
            'urls' => array(
                'catalog' => $this->createUrlBuilder('site/catalog')
                    ->getUrl(),
                // передана готовая строка адреса ?r=site/catalog
            ),
        ));
    }

    public function actionCatalog() {
        return $this->render('catalog', array(
            'products' => Product::model()->findAll(),
            'urls' => array(
                'product' => $this->createUrlBuilder('site/product')
                    ->setRequired(array('id')),
                // передан объект построителя с необходимым знанием о контроллере,
                // определены требуемые параметры, которые заполняются в представлении
            ),
        ));
    }

    public function actionProduct($id) {
        return $this->render('product');
    }
}


Представление вывода каталога товаров (catalog.php)
/** @var UrlBuilder $productUrlBuilder */
$productUrlBuilder = $this->getParam('urls.product');

foreach ($this->getParam('products') as $product) {
    $productUrl = $productUrlBuilder
        ->copy()
        ->setParam('id', $product->id)
        ->getUrl();

    print($productUrl);
    // строка адреса ?r=site/product&id=1
}

// или передать параметры построителя адреса в клиентский скрипт
$this->setJsParams(array(
    'urls' => array(
        'product' => $productUrlBuilder->toArray(),
    ),
));


Для удобства использования решения, код выложил на https://github.com/petrgrishin/yii-url-builder и https://packagist.org/packages/petrgrishin/yii-url-builder
Tags:
Hubs:
-11
Comments 4
Comments Comments 4

Articles