View difference between Paste ID: gA4ECi6p and Zf8g2Z7n
SHOW: | | - or go back to the newest paste.
1
*<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: hlogeon
5
 * Date: 13.08.14
6
 * Time: 12:33
7
 *
8
 * @author Degtyaruk Andrey <hlogeon1@gmail.com>
9
 *
10-
 * На своей основной работе я занимаюсь разработкой на Zend Framework\Zend Framework 2,
10+
11-
 * но я не очень  люблю Zend из-за его "академичности", описание множества вещей занимает куда
11+
12-
 * больше времени и строк кода, чем в других фреймворках. Однако, его академичность является для меня и плюсом,
12+
13-
 * ведь сам фреймворк, при правильном к нему подходе подталкивает использовать лучшие практики. При том,
13+
14-
 * как показывает опыт, довольно большая часть программистов на других фреймворках мало знакома даже с
14+
15-
 * базовыми концепциями инструментов, с которыми они работают и иногда не способны принимать, казалось бы очевидные,
15+
16-
 * но качественные архитектурные решения из-за привычки к тому, что фреймворк все делает за них.
16+
17-
 * Здесь я хочу привести пример, с которым я сталкивался, работая в довольно крупной компании.
17+
18-
 * Есть замечательный, на мой взгляд фреймворк - Yii Framework, который не только берет на
18+
19-
 * себя намного больше ответственности "из коробки", чем Zend, но и предоставляет довольно качественные и
19+
20-
 * гибкие интерфейсы для более тонкой настройки. А язык PHP, являясь очень гибким дополняет эту замечательную картину.
20+
21-
 * К сожалению, даже в крупных компаниях, где, казалось бы слова абстракция, принципы ООП, архитектура не должны являться пустым звуком, не используют эти возможности.
21+
22-
 * Для того, чтобы отобразить то, что делаю я и как это работает во многих других приложениях потребуется описать несколько
22+
23-
 * файлов, но для удобства я объединю каждое решение в 1 файл и добавлю дополнительные комментарии
23+
24-
 * на русском языке(в коде не пишу на русском, так будет видно, где пояснительные комментарии, а где те, которые были написаны при разработке)
24+
25
     */
26-
 * Этот файл описывает мои рещения
26+
27
28
    /**
29-
 * Довольно часто в приложениях необходимо выводить различные списки объектов,
29+
30-
 * первое что приходит на ум -  в каждом контроллере сделать listAction(), который и будет выводить список
30+
31-
 * объектов, которым управляет данный контроллер. Например, если у нас есть контроллер LinerController, отвечающий за лайнеры,
31+
32-
 * то для получения списка лайнеров, на первый взгляд, логично было бы сделать в этом контроллере метод listAction().
32+
33-
 * Но давайте посмотрим на другую сторону медали. Каждый раз нам придется писать listAction() в каждом контроллере? Что-то не так...
33+
34-
 * А как же быть с концепцией DRY?
34+
35
     */
36-
 * Решение довольно простое.
36+
37
38
    /**
39
     * If we have form on page that action renders - provide it here
40
     * @var string
41-
 * Для начала определим некое базовое действие, наследник стандартного для фреймворка действия CAction
41+
42
    public $formName = '';
43
44
45
    /**
46
     * Render with specified params
47
     */
48
    public function render()
49
    {
50
        if(Yii::app()->request->isAjaxRequest){
51
            $this->controller->renderPartial($this->view, $this->renderParams);
52
        }
53
        else{
54
            $this->controller->render($this->view, $this->renderParams);
55
        }
56
    }
57
58
    /**
59
     * Returns model by its ID
60
     *
61
     * @param $id
62
     *
63
     * @return EMongoDocument
64
     * @throws Exception
65
     *
66
     * @author Degtyaruk Andrey <hlogeon1@gmail.com>
67
     */
68
    public function getModelById($id)
69
    {
70
        if(is_string($id))
71
            $id = new MongoId($id);
72
        if(!$id instanceof MongoId)
73
            throw new Exception("ID is not instance of MongoId");
74
        return EMongoDocument::model($this->modelClass)->findByPk($id);
75
    }
76
77
    /**
78
     * Return modelClass
79
     *
80
     * @return EMongoDocument
81
     *
82
     * @author Degtyaruk Andrey <hlogeon1@gmail.com>
83
     */
84
    public function getModelClass()
85
    {
86
        return EMongoDocument::model($this->modelClass);
87
    }
88
89
    /**
90
     * @param $model
91
     *
92
     * @author Degtyaruk Andrey <hlogeon1@gmail.com>
93
     */
94
    protected function performAjaxValidation($model)
95
    {
96
        if(isset($_POST['ajax']))
97
        {
98
            echo CActiveForm::validate($model);
99
            Yii::app()->end();
100
        }
101
    }
102
103
}
104
105
106
/**
107
 * 
108
 * Created by PhpStorm.
109
 * User: hlogeon
110
 * Date: 21.06.14
111
 * Time: 14:57
112
 */
113
114
class BaseListAction extends BaseAction{
115
116
    /**
117
     * @var string
118
     */
119
    public $view = 'list';
120
121
    /**
122
     * @var array|EMongoCriteria predefined criteria
123
     */
124
    public $criteria = array();
125
126
127
    /**
128
     * @var string scenario for models
129
     */
130
    public $modelScenario = 'list';
131
132
    /**
133
     * @var array dataProvider config
134
     */
135-
 * Теперь, после создания базового класса для наших действий можно приступить к более конкретной реализации, а именно - создание базового класса для
135+
136-
 * вывода списков.
136+
137-
 * Именно здесь происходит вся "магия". Благодаря замечательному методу из CModel(EMongoDocument - его наследник) в Yii Framework - model(),
137+
138-
 * мы можем получить класс по его имени(без этого метода это тоже не проблема с использованием ReflectionClass). Наше действие стало довольно универсальным, давайте теперь посмотрим
138+
139-
 * как с ним работать.
139+
140
    public $dataProviderClass = 'EMongoDocumentDataProvider';
141
142
    /**
143
     * @var string filter class
144
     */
145
    public $filterClass;
146
147
    /**
148
     * @var string filter scenario
149
     */
150
    public $filterScenario = 'search';
151
152
153
    public function run()
154
    {
155
        /** @var $filter EMongoDocument */
156
        $filterClass = $this->filterClass ? $this->filterClass : $this->modelClass;
157
        $filter = new $filterClass($this->filterScenario);
158
        $filter->unsetAttributes();
159
        if($data = $this->getFilterData($filter))
160
            $filter->setAttributes($data);
161
        $filter->search();
162
163
        $filter->getDbCriteria()->mergeWith($this->criteria);
164
        /** @var $dataProvider EMongoDocumentDataProvider */
165
        $dataProviderClass = $this->dataProviderClass;
166
        $dataProvider = new $dataProviderClass($filter, $this->dataProviderConfig);
167
168
        self::setScenario($dataProvider->getData(), $this->modelScenario);
169
170
        $this->renderParams = array(
171
            'dataProvider' => $dataProvider,
172
            'filter' => $filter
173
        );
174
        $this->render();
175
    }
176
177
    public function getFilterData($filter)
178
    {
179
        $filterParams = Yii::app()->request->getParam(get_class($filter));
180
        return $filterParams;
181
    }
182
183
    /**
184
     * Set scenario to each model found
185
     * @param $data
186
     * @param $scenario
187
     */
188-
        // Первым делом создадим фильтр и установим параметры фильтрации из входных данных
188+
189
    {
190
        /**
191
         * @var EMongoDocument $model
192
         */
193
        foreach($data as $model){
194
            $model->setScenario($scenario);
195-
        $filter->search(); // Этот метод для того, чтобы критерия модели фильтра стала выбирать по установленным в модели атрибутам
195+
196
    }
197-
        // Теперь смержим критерию фильтра с предустановленной критерией
197+
198
}
199-
        // Теперь создадим дата провайдер. Дата провайдер из расширения yiimongodbsuite может брать критерию из
199+
200-
        // переданной ему модели (в нашем случае - фильтра)
200+
201
/**
202
 *
203
 * Created by PhpStorm.
204
 * User: hlogeon
205-
        // Теперь установим сценарии для моделей.
205+
206
 * Time: 3:12
207
 */
208
209
class CompanyController extends BackendController{
210
211
    public function actions()
212-
        // И выводим
212+
213
        return array(
214
            'index' => array(
215
                'class' => 'BaseListAction',
216
                'modelClass' => 'Company', 
217
                'view' => 'index',
218
            ),
219
        );
220
    }
221
}