Статьи

Создание PHP5 Framework: часть 2

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




В первой части этого руководства мы создали папку с названием controllers для хранения бизнес-логики для наших приложений. Как отметил Даок в комментарии, это не лучшее место для всей бизнес-логики, и что модель должна использоваться для хранения этой логики. Раньше я всегда использовал саму базу данных в качестве модели в большинстве своих приложений, однако выделение ее немного больше сделает нашу инфраструктуру еще более мощной и ее будет легче расширять.

Итак, что такое MVC ? MVC — это шаблон проектирования (как и шаблоны Singleton и Registry, которые мы рассмотрели в первой части), он обозначает Model View Controller, и цель этого шаблона состоит в том, чтобы отделить бизнес-логику, действия пользовательского интерфейса и пользовательский интерфейс от друг друга. Хотя мы пока не собираемся ничего делать с нашими моделями и контроллерами, давайте обновим структуру папок наших фреймворков, добавив в них папку «models». Модель будет содержать основную бизнес-логику, а контроллер будет заниматься взаимодействием с пользователем (например, отправкой данных, например комментарием). Примечание: нашу функцию __autoload менять не нужно.

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

Итак … что должен делать наш обработчик базы данных:

  • Управление подключениями к базе данных
  • Попробуйте обеспечить некоторый уровень абстракции от базы данных
  • Кэшируйте запросы, чтобы мы могли использовать их позже
  • Упростите общие операции с базами данных

Давайте посмотрим на код для нашего обработчика базы данных, а потом обсудим его.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<?php
 
/**
 * Database management and access class
 * This is a very basic level of abstraction
 */
class database {
     
    /**
     * Allows multiple database connections
     * probably not used very often by many applications, but still useful
     */
    private $connections = array();
     
    /**
     * Tells the DB object which connection to use
     * setActiveConnection($id) allows us to change this
     */
    private $activeConnection = 0;
     
    /**
     * Queries which have been executed and then «saved for later»
     */
    private $queryCache = array();
     
    /**
     * Data which has been prepared and then «saved for later»
     */
    private $dataCache = array();
     
    /**
     * Record of the last query
     */
    private $last;
     
     
    /**
     * Hello
     */
    public function __construct()
    {
         
    }
     
    /**
     * Create a new database connection
     * @param String database hostname
     * @param String database username
     * @param String database password
     * @param String database we are using
     * @return int the id of the new connection
     */
    public function newConnection( $host, $user, $password, $database )
    {
        $this->connections[] = new mysqli( $host, $user, $password, $database );
        $connection_id = count( $this->connections )-1;
        if( mysqli_connect_errno() )
        {
            trigger_error(‘Error connecting to host. ‘.$this->connections[$connection_id]->error, E_USER_ERROR);
        }
         
        return $connection_id;
    }
     
    /**
     * Close the active connection
     * @return void
     */
    public function closeConnection()
    {
        $this->connections[$this->activeConnection]->close();
    }
     
    /**
     * Change which database connection is actively used for the next operation
     * @param int the new connection id
     * @return void
     */
    public function setActiveConnection( int $new )
    {
        $this->activeConnection = $new;
    }
     
    /**
     * Store a query in the query cache for processing later
     * @param String the query string
     * @return the pointed to the query in the cache
     */
    public function cacheQuery( $queryStr )
    {
        if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) )
        {
            trigger_error(‘Error executing and caching query: ‘.$this->connections[$this->activeConnection]->error, E_USER_ERROR);
            return -1;
        }
        else
        {
            $this->queryCache[] = $result;
            return count($this->queryCache)-1;
        }
    }
     
    /**
     * Get the number of rows from the cache
     * @param int the query cache pointer
     * @return int the number of rows
     */
    public function numRowsFromCache( $cache_id )
    {
        return $this->queryCache[$cache_id]->num_rows;
    }
     
    /**
     * Get the rows from a cached query
     * @param int the query cache pointer
     * @return array the row
     */
    public function resultsFromCache( $cache_id )
    {
        return $this->queryCache[$cache_id]->fetch_array(MYSQLI_ASSOC);
    }
     
    /**
     * Store some data in a cache for later
     * @param array the data
     * @return int the pointed to the array in the data cache
     */
    public function cacheData( $data )
    {
        $this->dataCache[] = $data;
        return count( $this->dataCache )-1;
    }
     
    /**
     * Get data from the data cache
     * @param int data cache pointed
     * @return array the data
     */
    public function dataFromCache( $cache_id )
    {
        return $this->dataCache[$cache_id];
    }
     
    /**
     * Delete records from the database
     * @param String the table to remove rows from
     * @param String the condition for which rows are to be removed
     * @param int the number of rows to be removed
     * @return void
     */
    public function deleteRecords( $table, $condition, $limit )
    {
        $limit = ( $limit == » ) ?
        $delete = «DELETE FROM {$table} WHERE {$condition} {$limit}»;
        $this->executeQuery( $delete );
    }
     
    /**
     * Update records in the database
     * @param String the table
     * @param array of changes field => value
     * @param String the condition
     * @return bool
     */
    public function updateRecords( $table, $changes, $condition )
    {
        $update = «UPDATE » .
        foreach( $changes as $field => $value )
        {
            $update .= «`» .
        }
             
        // remove our trailing ,
        $update = substr($update, 0, -1);
        if( $condition != » )
        {
            $update .= «WHERE » .
        }
         
        $this->executeQuery( $update );
         
        return true;
         
    }
     
    /**
     * Insert records into the database
     * @param String the database table
     * @param array data to insert field => value
     * @return bool
     */
    public function insertRecords( $table, $data )
    {
        // setup some variables for fields and values
        $fields = «»;
        $values = «»;
         
        // populate them
        foreach ($data as $f => $v)
        {
             
            $fields .= «`$f`,»;
            $values .= ( is_numeric( $v ) && ( intval( $v ) == $v ) ) ?
         
        }
         
        // remove our trailing ,
        $fields = substr($fields, 0, -1);
        // remove our trailing ,
        $values = substr($values, 0, -1);
         
        $insert = «INSERT INTO $table ({$fields}) VALUES({$values})»;
        $this->executeQuery( $insert );
        return true;
    }
     
    /**
     * Execute a query string
     * @param String the query
     * @return void
     */
    public function executeQuery( $queryStr )
    {
        if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) )
        {
            trigger_error(‘Error executing query: ‘.$this->connections[$this->activeConnection]->error, E_USER_ERROR);
        }
        else
        {
            $this->last = $result;
        }
         
    }
     
    /**
     * Get the rows from the most recently executed query, excluding cached queries
     * @return array
     */
    public function getRows()
    {
        return $this->last->fetch_array(MYSQLI_ASSOC);
    }
     
    /**
     * Gets the number of affected rows from the previous query
     * @return int the number of affected rows
     */
    public function affectedRows()
    {
        return $this->$this->connections[$this->activeConnection]->affected_rows;
    }
     
    /**
     * Sanitize data
     * @param String the data to be sanitized
     * @return String the sanitized data
     */
    public function sanitizeData( $data )
    {
        return $this->connections[$this->activeConnection]->real_escape_string( $data );
    }
     
    /**
     * Deconstruct the object
     * close all of the database connections
     */
    public function __deconstruct()
    {
        foreach( $this->connections as $connection )
        {
            $connection->close();
        }
    }
}
?>

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

Наши методы удаления, вставки и обновления записи облегчают выполнение некоторых распространенных задач (как я упоминал выше, мы могли бы расширить это, чтобы сделать гораздо больше), предоставляя только такую ​​информацию, как имя таблицы, массив полей и значения, соответствующие ядру, предельные значения и условия. Запросы также можно «кэшировать», чтобы мы могли что-то сделать с ними позже. Я считаю, что эта функция (а также возможность «кэшировать» массивы данных) очень удобна в сочетании с менеджером шаблонов, так как мы можем легко перебирать строки данных и заполнять их в наших шаблонах без особых усилий, как вы будете посмотрим, когда мы посмотрим на менеджер шаблонов.

1
2
3
4
5
6
// insert record
$registry->getObject(‘db’)->insertRecords( ‘testTable’, array(‘name’=>’Michael’ ) );
// update a record
$registry->getObject(‘db’)->updateRecords( ‘testTable’, array(‘name’=>’MichaelP’ ), ‘ID=2’ );
// delete a record (well, upto 5 in this case)
$registry->getObject(‘db’)->deleteRecords( ‘testTable’, «name=’MichaelP'», 5 );

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

01
02
03
04
05
06
07
08
09
10
// our second database connection (let’s assume we already have a connection to our main DB)
$newConnection = $registry->getObject(‘db’)->newConnection(‘localhost’, ‘root’, ‘password’, ‘secondDB’);
// delete from the primary db connection
$registry->getObject(‘db’)->deleteRecords( ‘testTable’, «name=’MichaelP'», 5 );
// change our active db connection, to allow future queries to be on the second connection
$registry->getObject(‘db’)->setActiveConnection( $newConnection );
// delete from the secondary db connection
$registry->getObject(‘db’)->deleteRecords( ‘testTable’, «name=’MichaelP'», 5 );
// revert the active connection so future queries are on the primary db connection
$registry->getObject(‘db’)->setActiveConnection( 0 );

Как мы можем расширить этот класс?

  • Полная абстракция
  • Используйте наследование, создайте интерфейс и наследуйте от него классы базы данных, каждый для разных механизмов базы данных
  • Сохраняйте идентификаторы соединения вместе с запросом при кэшировании запросов
  • Улучшение санации данных в зависимости от типа данных, которые мы хотим санировать

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

Чтобы упростить задачу, мы будем использовать класс страницы для содержания, связанного со страницей, это также облегчит нам его расширение и добавление функций к нему позже. Менеджер шаблонов будет управлять этим объектом.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<?php
 
// prevent this file being called directly
if ( ! defined( ‘PCAFW’ ) )
{
    echo ‘This file can only be called via the main index.php file, and not directly’;
    exit();
}
 
/**
 * Template manager class
 */
class template {
 
    private $page;
     
    /**
     * Hello!
     */
    public function __construct()
    {
        include( APP_PATH . ‘/PCARegistry/objects/page.class.php’);
        $this->page = new Page();
 
    }
     
    /**
     * Add a template bit onto our page
     * @param String $tag the tag where we insert the template eg {hello}
     * @param String $bit the template bit (path to file, or just the filename)
     * @return void
     */
    public function addTemplateBit( $tag, $bit )
    {
        if( strpos( $bit, ‘skins/’ ) === false )
        {
            $bit = ‘skins/’ .
        }
        $this->page->addTemplateBit( $tag, $bit );
    }
     
    /**
     * Put the template bits into our page content
     * Updates the pages content
     * @return void
     */
    private function replaceBits()
    {
        $bits = $this->page->getBits();
        foreach( $bits as $tag => $template )
        {
            $templateContent = file_get_contents( $bit );
            $newContent = str_replace( ‘{‘ . $tag . ‘}’, $templateContent, $this->page->getContent() );
            $this->page->setContent( $newContent );
        }
    }
     
    /**
     * Replace tags in our page with content
     * @return void
     */
    private function replaceTags()
    {
        // get the tags
        $tags = $this->page->getTags();
        // go through them all
        foreach( $tags as $tag => $data )
        {
            if( is_array( $data ) )
            {
               
                if( $data[0] == ‘SQL’ )
                {
                    // it is a cached query…replace DB tags
                    $this->replaceDBTags( $tag, $data[1] );
                }
                elseif( $data[0] == ‘DATA’ )
                {
                     // it is some cached data…replace data tags
                    $this->replaceDataTags( $tag, $data[1] );
                }
            }
            else
            {
                // replace the content
                $newContent = str_replace( ‘{‘ . $tag . ‘}’, $data, $this->page->getContent() );
                // update the pages content
                $this->page->setContent( $newContent );
            }
        }
    }
     
    /**
     * Replace content on the page with data from the DB
     * @param String $tag the tag defining the area of content
     * @param int $cacheId the queries ID in the query cache
     * @return void
     */
    private function replaceDBTags( $tag, $cacheId )
    {
        $block = »;
        $blockOld = $this->page->getBlock( $tag );
         
        // foreach record relating to the query…
        while ($tags = PCARegistry::getObject(‘db’)->resultsFromCache( $cacheId ) )
        {
            $blockNew = $blockOld;
            // create a new block of content with the results replaced into it
            foreach ($tags as $ntag => $data)
            {
                $blockNew = str_replace(«{» . $ntag . «}», $data, $blockNew);
            }
            $block .= $blockNew;
        }
        $pageContent = $this->page->getContent();
        // remove the seperator in the template, cleaner HTML
        $newContent = str_replace( ‘<!— START ‘ . $tag . ‘ —>’ . $blockOld . ‘<!— END ‘ . $tag . ‘ —>’, $block, $pageContent );
        // update the page content
        $this->page->setContent( $newContent );
    }
     
    /**
     * Replace content on the page with data from the cache
     * @param String $tag the tag defining the area of content
     * @param int $cacheId the datas ID in the data cache
     * @return void
     */
    private function replaceDataTags( $tag, $cacheId )
    {
        $block = $this->page->getBlock( $tag );
        $blockOld = $block;
        while ($tags = PCARegistry::getObject(‘db’)->dataFromCache( $cacheId ) )
        {
            foreach ($tags as $tag => $data)
            {
                $blockNew = $blockOld;
                $blockNew = str_replace(«{» . $tag . «}», $data, $blockNew);
            }
            $block .= $blockNew;
        }
        $pageContent = $this->page->getContent();
        $newContent = str_replace( $blockOld, $block, $pageContent );
        $this->page->setContent( $newContent );
    }
     
    /**
     * Get the page object
     * @return Object
     */
    public function getPage()
    {
        return $this->page;
    }
     
    /**
     * Set the content of the page based on a number of templates
     * pass template file locations as individual arguments
     * @return void
     */
    public function buildFromTemplates()
    {
        $bits = func_get_args();
        $content = «»;
        foreach( $bits as $bit )
        {
             
            if( strpos( $bit, ‘skins/’ ) === false )
            {
                $bit = ‘skins/’ .
            }
            if( file_exists( $bit ) == true )
            {
                $content .= file_get_contents( $bit );
            }
             
        }
        $this->page->setContent( $content );
    }
     
    /**
     * Convert an array of data (ie a db row?) to some tags
     * @param array the data
     * @param string a prefix which is added to field name to create the tag name
     * @return void
     */
    public function dataToTags( $data, $prefix )
    {
        foreach( $data as $key => $content )
        {
            $this->page->addTag( $key.$prefix, $content);
        }
    }
     
    public function parseTitle()
    {
        $newContent = str_replace(‘<title>’, ‘<title>’. $page->getTitle(), $this->page->getContent() );
        $this->page->setContent( $newContent );
    }
     
    /**
     * Parse the page object into some output
     * @return void
     */
    public function parseOutput()
    {
        $this->replaceBits();
        $this->replaceTags();
        $this->parseTitle();
    }
     
     
     
}
?>

Итак, что именно делает этот класс?

Создает наш объект страницы и основывает его на файлах шаблонов. Объект страницы содержит содержимое и информацию, необходимые для создания HTML-кода страницы. Затем мы собираем buildFromTemplate (‘templatefile.tpl.php’, ‘templatefile2.tpl.php’), чтобы получить начальный контент для нашей страницы, этот метод принимает любое количество файлов шаблона в качестве аргументов и объединяет их по порядку, что полезно для шаблоны заголовка, содержимого и нижнего колонтитула.

Управляет контентом, связанным со страницей , помогая объекту страницы поддерживать запись данных, подлежащих замене на странице, а также дополнительные биты шаблона, которые необходимо встроить в страницу (addTemplateBit (‘userbar’, ‘usertoolsbar.tpl.php). «)).

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

Файл шаблона должен отмечать внутри себя, где должен быть извлечен кэшированный запрос и заменены данные из запроса. Когда менеджер шаблонов встречает тег для замены, являющийся запросом, он получает часть страницы, на которой ему необходимо выполнить итерацию, вызывая getBlock (‘block’) для объекта страницы. Этот кусок контента затем копируется для каждой записи в запросе, и в нем есть теги, заменяемые результатами запроса. Мы посмотрим, как это будет выглядеть в шаблоне позже в этом уроке.

Объект страницы управляется менеджером шаблонов и используется для хранения всех деталей, связанных со страницей. Это позволяет управлять менеджером шаблонов, в то же время упрощая для нас расширение функциональности этого на более позднем этапе.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?php
 
/**
 * This is our page object
 * It is a seperate object to allow some interesting extra functionality to be added
 * Some ideas: passwording pages, adding page specific css/js files, etc
 */
class page {
 
    // room to grow later?
    private $css = array();
    private $js = array();
    private $bodyTag = »;
    private $bodyTagInsert = »;
     
    // future functionality?
    private $authorised = true;
    private $password = »;
     
    // page elements
    private $title = »;
    private $tags = array();
    private $postParseTags = array();
    private $bits = array();
    private $content = «»;
     
    /**
     * Constructor…
     */
    function __construct() { }
     
    public function getTitle()
    {
        return $this->title;
    }
     
    public function setPassword( $password )
    {
        $this->password = $password;
    }
     
    public function setTitle( $title )
    {
        $this->title = $title;
    }
     
    public function setContent( $content )
    {
        $this->content = $content;
    }
     
    public function addTag( $key, $data )
    {
        $this->tags[$key] = $data;
    }
     
    public function getTags()
    {
        return $this->tags;
    }
     
    public function addPPTag( $key, $data )
    {
        $this->postParseTags[$key] = $data;
    }
     
    /**
     * Get tags to be parsed after the first batch have been parsed
     * @return array
     */
    public function getPPTags()
    {
        return $this->postParseTags;
    }
     
    /**
     * Add a template bit to the page, doesnt actually add the content just yet
     * @param String the tag where the template is added
     * @param String the template file name
     * @return void
     */
    public function addTemplateBit( $tag, $bit )
    {
        $this->bits[ $tag ] = $bit;
    }
     
    /**
     * Get the template bits to be entered into the page
     * @return array the array of template tags and template file names
     */
    public function getBits()
    {
        return $this->bits;
    }
     
    /**
     * Gets a chunk of page content
     * @param String the tag wrapping the block ( <!— START tag —> block <!— END tag —> )
     * @return String the block of content
     */
    public function getBlock( $tag )
    {
        preg_match (‘#<!— START ‘. $tag . ‘ —>(.+?)<!— END ‘. $tag . ‘ —>#si’, $this->content, $tor);
         
        $tor = str_replace (‘<!— START ‘. $tag . ‘ —>’, «», $tor[0]);
        $tor = str_replace (‘<!— END ‘ . $tag . ‘ —>’, «», $tor);
         
        return $tor;
    }
     
    public function getContent()
    {
        return $this->content;
    }
   
}
?>

Как этот класс можно расширить и улучшить?

  • PostParseTags: Вы можете захотеть заменить теги после того, как большая часть страницы будет проанализирована, возможно, содержимое в базе данных содержит теги, которые необходимо проанализировать.
  • Страницы с паролем: назначьте пароль странице, проверьте, есть ли у пользователя пароль в файле cookie или сеансе, чтобы они могли видеть страницу.
  • Страницы с ограниченным доступом (хотя сначала нам нужны наши компоненты аутентификации!)
  • Изменение
  • Динамическое добавление ссылок на файлы JavaScript и CSS на основе страницы или приложения.

Теперь, когда у нас есть некоторые объекты, которые наш реестр собирается хранить для нас, нам нужно сообщить реестру, какие это объекты. Я создал метод в объекте PCARegistry под названием loadCoreObjects, который (как говорится) загружает основные объекты. Это означает, что можно просто вызвать это из нашего файла index.php, чтобы загрузить реестр с этими объектами.

1
2
3
4
5
public function storeCoreObjects()
{
    $this->storeObject(‘database’, ‘db’ );
    $this->storeObject(‘template’, ‘template’ );
}

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

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

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

Очевидно, нам нужно несколько строк данных в этой таблице!

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<html>
<head>
    <title> Powered by PCA Framework</title>
</head>
<body>
<h1>Our Members</h1>
<p>Below is a list of our members:</p>
<ul>
<!— START members —>
<li>{name} {email}</li>
<!— END members —>
</ul>
</body>
</html>

HTML-комментарии членов START и END обозначают блок members (который получается с помощью метода getBlock () на странице), здесь менеджер шаблонов будет перебирать записи в базе данных и отображать их.

Теперь нам нужно собрать все это вместе с нашим файлом index.php:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// require our registry
require_once(‘PCARegistry/pcaregistry.class.php’);
$registry = PCARegistry::singleton();
 
// store those core objects
$registry->storeCoreObjects();
 
// create a database connection
$registry->getObject(‘db’)->newConnection(‘localhost’, ‘root’, », ‘pcaframework’);
 
// set the default skin setting (we will store these in the database later…)
$registry->storeSetting(‘default’, ‘skin’);
 
// populate our page object from a template file
$registry->getObject(‘template’)->buildFromTemplates(‘main.tpl.php’);
 
// cache a query of our members table
$cache = $registry->getObject(‘db’)->cacheQuery(‘SELECT * FROM members’);
 
// assign this to the members tag
$registry->getObject(‘template’)->getPage()->addTag(‘members’, array(‘SQL’, $cache) );
 
// set the page title
$registry->getObject(‘template’)->getPage()->setTitle(‘Our members’);
 
// parse it all, and spit it out
$registry->getObject(‘template’)->parseOutput();
print $registry->getObject(‘template’)->getPage()->getContent();

Если мы теперь просмотрим эту страницу в нашем веб-браузере, результаты запроса отобразятся на странице:

В третьей части мы немного отклонимся от стороны разработки нашей платформы и рассмотрим, как проектировать с учетом нашей инфраструктуры и как нарезать шаблоны HTML так, чтобы они подходили для нашей платформы. Когда мы начнем создавать наше первое приложение с нашей платформой, мы более подробно рассмотрим некоторые из этих классов. Наконец, спасибо за ваши комментарии в прошлый раз!