() translation by (you can also view the original English article)
Sa pangunahing istraktura para sa ating Framework na matatag na, panahon na para simulang maglagay ng functionality dito. Sa pagtuturong ito gagawa tayo ng template manager at database handler, na magdadala sa atin sa magaling na Framework na akma sa paggamit ng halos anong proyekto. Kung wala ka pa, siguraduhin na suriin muna ang Unang Bahagi ng seryeng ito!
MVC: I-tweak ang istraktura
Sa unang bahagi ng pagtuturong ito,gumawa tayo ng folder na tinawag na controllers para i-store ang business logic para sa ating applications. Katulad ng binigyang punto ni daok sa komento, hindi ito ang pinakamagandang lugar para sa lahat ng business logic, at kailangang gamitin ang modelo para i-store itong logic. Dati,lagi kong ginagamit ang database mismo bilang modelo sa karamihan sa aking applications,gayunpaman,ang pagbubukod nito ng kaunti ay gagawing mas magaling ang ating framework, at mas madaling pahabain.
Kung kaya ano ang MVC? Ang MVC ay isang pattern ng disenyo (katulad ng Singleton at Registry patterns na tiningnan natin sa unang bahagi), at ang ibig sabihin ay Model View Controller, at ang layunin ay paghiwalayin ang business logic, user interface actions at ang user interface mula sa isa’t isa. Bagaman wala tayong ibang gagawin sa ating mga modelo at controllers sa ngayon, i-update natin ang istraktura ng ating frameworks folder para mailagay ang “models’ folder. Ang modelo ay maglalaman ng pangunahing business logic, at ang controller ang bahala sa user interaction (halimbawa pagpasa ng data, katulad ng komento). NB: Ang ating__autoload function ay hindi kailangang mabago.



Database Handler
Karamihan sa websites at web applications na gumagamit ng PHP ay gumagamit din ng database engine, katulad ng MySQL. Kung pananatilihin natin lahat ng may kaugnayan sa database functions sa parehong kinalalagyan, kung gayon maaari nating (sa teorya) mabilis na mabago ang database engine na ginagamit natin. Maaari din nating gawing mas madali ang ilang operasyon, katulad ng paglakip ng records, pag-update ng records at pagtanggal ng records mula sa database. Mas mapapadali din nito ang pagtatrabaho sa iba’t ibang koneksyon ng database.
Kung kaya, ano ang dapat gawin ng ating database handler:
- I-manage ang mga koneksyon sa database
- Subukang magbigay ng ilang antas ng abstraction mula sa database
- Cache queries para magamit natin sila sa susunod
- Gawing mas madali ang karaniwang operasyon sa database
Tingnan natin ang code para sa ating database handler, pagkatapos ay tatalakayin natin ito.
1 |
|
2 |
<?php
|
3 |
|
4 |
/**
|
5 |
* Database management and access class
|
6 |
* This is a very basic level of abstraction
|
7 |
*/
|
8 |
class database { |
9 |
|
10 |
/**
|
11 |
* Allows multiple database connections
|
12 |
* probably not used very often by many applications, but still useful
|
13 |
*/
|
14 |
private $connections = array(); |
15 |
|
16 |
/**
|
17 |
* Tells the DB object which connection to use
|
18 |
* setActiveConnection($id) allows us to change this
|
19 |
*/
|
20 |
private $activeConnection = 0; |
21 |
|
22 |
/**
|
23 |
* Queries which have been executed and then "saved for later"
|
24 |
*/
|
25 |
private $queryCache = array(); |
26 |
|
27 |
/**
|
28 |
* Data which has been prepared and then "saved for later"
|
29 |
*/
|
30 |
private $dataCache = array(); |
31 |
|
32 |
/**
|
33 |
* Record of the last query
|
34 |
*/
|
35 |
private $last; |
36 |
|
37 |
|
38 |
/**
|
39 |
* Hello
|
40 |
*/
|
41 |
public function __construct() |
42 |
{
|
43 |
|
44 |
}
|
45 |
|
46 |
/**
|
47 |
* Create a new database connection
|
48 |
* @param String database hostname
|
49 |
* @param String database username
|
50 |
* @param String database password
|
51 |
* @param String database we are using
|
52 |
* @return int the id of the new connection
|
53 |
*/
|
54 |
public function newConnection( $host, $user, $password, $database ) |
55 |
{
|
56 |
$this->connections[] = new mysqli( $host, $user, $password, $database ); |
57 |
$connection_id = count( $this->connections )-1; |
58 |
if( mysqli_connect_errno() ) |
59 |
{
|
60 |
trigger_error('Error connecting to host. '.$this->connections[$connection_id]->error, E_USER_ERROR); |
61 |
}
|
62 |
|
63 |
return $connection_id; |
64 |
}
|
65 |
|
66 |
/**
|
67 |
* Close the active connection
|
68 |
* @return void
|
69 |
*/
|
70 |
public function closeConnection() |
71 |
{
|
72 |
$this->connections[$this->activeConnection]->close(); |
73 |
}
|
74 |
|
75 |
/**
|
76 |
* Change which database connection is actively used for the next operation
|
77 |
* @param int the new connection id
|
78 |
* @return void
|
79 |
*/
|
80 |
public function setActiveConnection( int $new ) |
81 |
{
|
82 |
$this->activeConnection = $new; |
83 |
}
|
84 |
|
85 |
/**
|
86 |
* Store a query in the query cache for processing later
|
87 |
* @param String the query string
|
88 |
* @return the pointed to the query in the cache
|
89 |
*/
|
90 |
public function cacheQuery( $queryStr ) |
91 |
{
|
92 |
if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) ) |
93 |
{
|
94 |
trigger_error('Error executing and caching query: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR); |
95 |
return -1; |
96 |
}
|
97 |
else
|
98 |
{
|
99 |
$this->queryCache[] = $result; |
100 |
return count($this->queryCache)-1; |
101 |
}
|
102 |
}
|
103 |
|
104 |
/**
|
105 |
* Get the number of rows from the cache
|
106 |
* @param int the query cache pointer
|
107 |
* @return int the number of rows
|
108 |
*/
|
109 |
public function numRowsFromCache( $cache_id ) |
110 |
{
|
111 |
return $this->queryCache[$cache_id]->num_rows; |
112 |
}
|
113 |
|
114 |
/**
|
115 |
* Get the rows from a cached query
|
116 |
* @param int the query cache pointer
|
117 |
* @return array the row
|
118 |
*/
|
119 |
public function resultsFromCache( $cache_id ) |
120 |
{
|
121 |
return $this->queryCache[$cache_id]->fetch_array(MYSQLI_ASSOC); |
122 |
}
|
123 |
|
124 |
/**
|
125 |
* Store some data in a cache for later
|
126 |
* @param array the data
|
127 |
* @return int the pointed to the array in the data cache
|
128 |
*/
|
129 |
public function cacheData( $data ) |
130 |
{
|
131 |
$this->dataCache[] = $data; |
132 |
return count( $this->dataCache )-1; |
133 |
}
|
134 |
|
135 |
/**
|
136 |
* Get data from the data cache
|
137 |
* @param int data cache pointed
|
138 |
* @return array the data
|
139 |
*/
|
140 |
public function dataFromCache( $cache_id ) |
141 |
{
|
142 |
return $this->dataCache[$cache_id]; |
143 |
}
|
144 |
|
145 |
/**
|
146 |
* Delete records from the database
|
147 |
* @param String the table to remove rows from
|
148 |
* @param String the condition for which rows are to be removed
|
149 |
* @param int the number of rows to be removed
|
150 |
* @return void
|
151 |
*/
|
152 |
public function deleteRecords( $table, $condition, $limit ) |
153 |
{
|
154 |
$limit = ( $limit == '' ) ? '' : ' LIMIT ' . $limit; |
155 |
$delete = "DELETE FROM {$table} WHERE {$condition} {$limit}"; |
156 |
$this->executeQuery( $delete ); |
157 |
}
|
158 |
|
159 |
/**
|
160 |
* Update records in the database
|
161 |
* @param String the table
|
162 |
* @param array of changes field => value
|
163 |
* @param String the condition
|
164 |
* @return bool
|
165 |
*/
|
166 |
public function updateRecords( $table, $changes, $condition ) |
167 |
{
|
168 |
$update = "UPDATE " . $table . " SET "; |
169 |
foreach( $changes as $field => $value ) |
170 |
{
|
171 |
$update .= "`" . $field . "`='{$value}',"; |
172 |
}
|
173 |
|
174 |
// remove our trailing ,
|
175 |
$update = substr($update, 0, -1); |
176 |
if( $condition != '' ) |
177 |
{
|
178 |
$update .= "WHERE " . $condition; |
179 |
}
|
180 |
|
181 |
$this->executeQuery( $update ); |
182 |
|
183 |
return true; |
184 |
|
185 |
}
|
186 |
|
187 |
/**
|
188 |
* Insert records into the database
|
189 |
* @param String the database table
|
190 |
* @param array data to insert field => value
|
191 |
* @return bool
|
192 |
*/
|
193 |
public function insertRecords( $table, $data ) |
194 |
{
|
195 |
// setup some variables for fields and values
|
196 |
$fields = ""; |
197 |
$values = ""; |
198 |
|
199 |
// populate them
|
200 |
foreach ($data as $f => $v) |
201 |
{
|
202 |
|
203 |
$fields .= "`$f`,"; |
204 |
$values .= ( is_numeric( $v ) && ( intval( $v ) == $v ) ) ? $v."," : "'$v',"; |
205 |
|
206 |
}
|
207 |
|
208 |
// remove our trailing ,
|
209 |
$fields = substr($fields, 0, -1); |
210 |
// remove our trailing ,
|
211 |
$values = substr($values, 0, -1); |
212 |
|
213 |
$insert = "INSERT INTO $table ({$fields}) VALUES({$values})"; |
214 |
$this->executeQuery( $insert ); |
215 |
return true; |
216 |
}
|
217 |
|
218 |
/**
|
219 |
* Execute a query string
|
220 |
* @param String the query
|
221 |
* @return void
|
222 |
*/
|
223 |
public function executeQuery( $queryStr ) |
224 |
{
|
225 |
if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) ) |
226 |
{
|
227 |
trigger_error('Error executing query: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR); |
228 |
}
|
229 |
else
|
230 |
{
|
231 |
$this->last = $result; |
232 |
}
|
233 |
|
234 |
}
|
235 |
|
236 |
/**
|
237 |
* Get the rows from the most recently executed query, excluding cached queries
|
238 |
* @return array
|
239 |
*/
|
240 |
public function getRows() |
241 |
{
|
242 |
return $this->last->fetch_array(MYSQLI_ASSOC); |
243 |
}
|
244 |
|
245 |
/**
|
246 |
* Gets the number of affected rows from the previous query
|
247 |
* @return int the number of affected rows
|
248 |
*/
|
249 |
public function affectedRows() |
250 |
{
|
251 |
return $this->$this->connections[$this->activeConnection]->affected_rows; |
252 |
}
|
253 |
|
254 |
/**
|
255 |
* Sanitize data
|
256 |
* @param String the data to be sanitized
|
257 |
* @return String the sanitized data
|
258 |
*/
|
259 |
public function sanitizeData( $data ) |
260 |
{
|
261 |
return $this->connections[$this->activeConnection]->real_escape_string( $data ); |
262 |
}
|
263 |
|
264 |
/**
|
265 |
* Deconstruct the object
|
266 |
* close all of the database connections
|
267 |
*/
|
268 |
public function __deconstruct() |
269 |
{
|
270 |
foreach( $this->connections as $connection ) |
271 |
{
|
272 |
$connection->close(); |
273 |
}
|
274 |
}
|
275 |
}
|
276 |
?>
|
Bago natin talakayin ito ng mas detalyado, kailangan kong bigyang-diin na ang database handler na ito ay napaka basic.Maaari tayong makapagbigay ng kumpletong abstraction sa pamamagitan ng pag-execute ng queries ng direkta, subalit sa halip ay pagbuo ng queries batay sa parameters sa query function, at pagkatapos ay pag-execute nito.
Sa ating delete, insert at update record methods ay nagiging mas madali na gawin ang ilang karaniwang trabaho (katulad ng nabanggit ko sa itaas maaari natin itong pahabain para mas madaming magawa), sa pamamagitan ng pagbibigay lang ng impormasyon katulad ng pangalan ng table, array of fields at katumbas na values, limit values at conditions. Ang Queries ay maaari ding i-cached para may magawa tayo dito sa susunod. Napagtanto ko na ang feature na ito (at gayon din ang kakayahan na i-cache ang arrays of data) ay kapaki-pakinabang kapag pinagsama sa template manager, dahil madali nating mauulit sa pamamagitan ng hanay ng data at paglagyan ito ng sa ating templates na may kaunting pagkaabala, katulad ng makikita mo kapag tiningnan natin ang template manager.
1 |
|
2 |
// insert record
|
3 |
$registry->getObject('db')->insertRecords( 'testTable', array('name'=>'Michael' ) ); |
4 |
// update a record
|
5 |
$registry->getObject('db')->updateRecords( 'testTable', array('name'=>'MichaelP' ), 'ID=2' ); |
6 |
// delete a record (well, upto 5 in this case)
|
7 |
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 ); |
Maaari din tayong gumawa sa iba’t ibang koneksyon ng database ng medyo madali, basta mailipat natin sa pagitan ng tamang koneksyon kung kailangan (bagaman ay hindi ito uubra kapag magka-cache ng queries at ire-retrieve ang mga ito sa pamamagitan ng template manager ng walang trabahong gagawin), halimbawa, sa code snippet sa ibaba ay matatanggal natin ang records mula sa dalawang databases.
1 |
|
2 |
// our second database connection (let's assume we already have a connection to our main DB)
|
3 |
$newConnection = $registry->getObject('db')->newConnection('localhost', 'root', 'password', 'secondDB'); |
4 |
// delete from the primary db connection
|
5 |
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 ); |
6 |
// change our active db connection, to allow future queries to be on the second connection
|
7 |
$registry->getObject('db')->setActiveConnection( $newConnection ); |
8 |
// delete from the secondary db connection
|
9 |
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 ); |
10 |
// revert the active connection so future queries are on the primary db connection
|
11 |
$registry->getObject('db')->setActiveConnection( 0 ); |
Paano natin gustong pahabain ang class na ito?
- Full abstraction
- Gamitin ang inheritance, gumawa ng interface at gawing ma-inherit ito ng database classes mula dito, sa bawat isa para sa iba’t ibang database engines
- I-store ang connection ID’s kasama ng query kapag magka-cache ng queries
- Mas pagandahin ang data sanitizing, depende sa uri ng data na gusto nating i-sanitize
Ang template manager
Ang template manager na ang bahala sa lahat ng output, kailangan nitong gumana sa iba’t ibang template files, palitan ang placeholders (tinatawag ko silang tags) na may data at ulitin sa pamamagitan ng mga bahagi ng template na may iba’t ibang hanay ng data mula sa database.
Para mas mapadali ang mga bagay, gagamit tayo ng page class para maghawak ng content na may kinalaman sa page, mas mapapadali din nito para sa atin na mapahaba at makapaglagay ng features dito sa susunod. Ang template manager ang bahala na sa object na ito.
1 |
|
2 |
<?php
|
3 |
|
4 |
// prevent this file being called directly
|
5 |
if ( ! defined( 'PCAFW' ) ) |
6 |
{
|
7 |
echo 'This file can only be called via the main index.php file, and not directly'; |
8 |
exit(); |
9 |
}
|
10 |
|
11 |
/**
|
12 |
* Template manager class
|
13 |
*/
|
14 |
class template { |
15 |
|
16 |
private $page; |
17 |
|
18 |
/**
|
19 |
* Hello!
|
20 |
*/
|
21 |
public function __construct() |
22 |
{
|
23 |
include( APP_PATH . '/PCARegistry/objects/page.class.php'); |
24 |
$this->page = new Page(); |
25 |
|
26 |
}
|
27 |
|
28 |
/**
|
29 |
* Add a template bit onto our page
|
30 |
* @param String $tag the tag where we insert the template e.g. {hello}
|
31 |
* @param String $bit the template bit (path to file, or just the filename)
|
32 |
* @return void
|
33 |
*/
|
34 |
public function addTemplateBit( $tag, $bit ) |
35 |
{
|
36 |
if( strpos( $bit, 'skins/' ) === false ) |
37 |
{
|
38 |
$bit = 'skins/' . PCARegistry::getSetting('skin') . '/templates/' . $bit; |
39 |
}
|
40 |
$this->page->addTemplateBit( $tag, $bit ); |
41 |
}
|
42 |
|
43 |
/**
|
44 |
* Put the template bits into our page content
|
45 |
* Updates the pages content
|
46 |
* @return void
|
47 |
*/
|
48 |
private function replaceBits() |
49 |
{
|
50 |
$bits = $this->page->getBits(); |
51 |
foreach( $bits as $tag => $template ) |
52 |
{
|
53 |
$templateContent = file_get_contents( $bit ); |
54 |
$newContent = str_replace( '{' . $tag . '}', $templateContent, $this->page->getContent() ); |
55 |
$this->page->setContent( $newContent ); |
56 |
}
|
57 |
}
|
58 |
|
59 |
/**
|
60 |
* Replace tags in our page with content
|
61 |
* @return void
|
62 |
*/
|
63 |
private function replaceTags() |
64 |
{
|
65 |
// get the tags
|
66 |
$tags = $this->page->getTags(); |
67 |
// go through them all
|
68 |
foreach( $tags as $tag => $data ) |
69 |
{
|
70 |
if( is_array( $data ) ) |
71 |
{
|
72 |
|
73 |
if( $data[0] == 'SQL' ) |
74 |
{
|
75 |
// it is a cached query...replace DB tags
|
76 |
$this->replaceDBTags( $tag, $data[1] ); |
77 |
}
|
78 |
elseif( $data[0] == 'DATA' ) |
79 |
{
|
80 |
// it is some cached data...replace data tags
|
81 |
$this->replaceDataTags( $tag, $data[1] ); |
82 |
}
|
83 |
}
|
84 |
else
|
85 |
{
|
86 |
// replace the content
|
87 |
$newContent = str_replace( '{' . $tag . '}', $data, $this->page->getContent() ); |
88 |
// update the pages content
|
89 |
$this->page->setContent( $newContent ); |
90 |
}
|
91 |
}
|
92 |
}
|
93 |
|
94 |
/**
|
95 |
* Replace content on the page with data from the DB
|
96 |
* @param String $tag the tag defining the area of content
|
97 |
* @param int $cacheId the queries ID in the query cache
|
98 |
* @return void
|
99 |
*/
|
100 |
private function replaceDBTags( $tag, $cacheId ) |
101 |
{
|
102 |
$block = ''; |
103 |
$blockOld = $this->page->getBlock( $tag ); |
104 |
|
105 |
// foreach record relating to the query...
|
106 |
while ($tags = PCARegistry::getObject('db')->resultsFromCache( $cacheId ) ) |
107 |
{
|
108 |
$blockNew = $blockOld; |
109 |
// create a new block of content with the results replaced into it
|
110 |
foreach ($tags as $ntag => $data) |
111 |
{
|
112 |
$blockNew = str_replace("{" . $ntag . "}", $data, $blockNew); |
113 |
}
|
114 |
$block .= $blockNew; |
115 |
}
|
116 |
$pageContent = $this->page->getContent(); |
117 |
// remove the seperator in the template, cleaner HTML
|
118 |
$newContent = str_replace( '<!-- START ' . $tag . ' -->' . $blockOld . '<!-- END ' . $tag . ' -->', $block, $pageContent ); |
119 |
// update the page content
|
120 |
$this->page->setContent( $newContent ); |
121 |
}
|
122 |
|
123 |
/**
|
124 |
* Replace content on the page with data from the cache
|
125 |
* @param String $tag the tag defining the area of content
|
126 |
* @param int $cacheId the datas ID in the data cache
|
127 |
* @return void
|
128 |
*/
|
129 |
private function replaceDataTags( $tag, $cacheId ) |
130 |
{
|
131 |
$block = $this->page->getBlock( $tag ); |
132 |
$blockOld = $block; |
133 |
while ($tags = PCARegistry::getObject('db')->dataFromCache( $cacheId ) ) |
134 |
{
|
135 |
foreach ($tags as $tag => $data) |
136 |
{
|
137 |
$blockNew = $blockOld; |
138 |
$blockNew = str_replace("{" . $tag . "}", $data, $blockNew); |
139 |
}
|
140 |
$block .= $blockNew; |
141 |
}
|
142 |
$pageContent = $this->page->getContent(); |
143 |
$newContent = str_replace( $blockOld, $block, $pageContent ); |
144 |
$this->page->setContent( $newContent ); |
145 |
}
|
146 |
|
147 |
/**
|
148 |
* Get the page object
|
149 |
* @return Object
|
150 |
*/
|
151 |
public function getPage() |
152 |
{
|
153 |
return $this->page; |
154 |
}
|
155 |
|
156 |
/**
|
157 |
* Set the content of the page based on a number of templates
|
158 |
* pass template file locations as individual arguments
|
159 |
* @return void
|
160 |
*/
|
161 |
public function buildFromTemplates() |
162 |
{
|
163 |
$bits = func_get_args(); |
164 |
$content = ""; |
165 |
foreach( $bits as $bit ) |
166 |
{
|
167 |
|
168 |
if( strpos( $bit, 'skins/' ) === false ) |
169 |
{
|
170 |
$bit = 'skins/' . PCARegistry::getSetting('skin') . '/templates/' . $bit; |
171 |
}
|
172 |
if( file_exists( $bit ) == true ) |
173 |
{
|
174 |
$content .= file_get_contents( $bit ); |
175 |
}
|
176 |
|
177 |
}
|
178 |
$this->page->setContent( $content ); |
179 |
}
|
180 |
|
181 |
/**
|
182 |
* Convert an array of data (i.e. a db row?) to some tags
|
183 |
* @param array the data
|
184 |
* @param string a prefix which is added to field name to create the tag name
|
185 |
* @return void
|
186 |
*/
|
187 |
public function dataToTags( $data, $prefix ) |
188 |
{
|
189 |
foreach( $data as $key => $content ) |
190 |
{
|
191 |
$this->page->addTag( $key.$prefix, $content); |
192 |
}
|
193 |
}
|
194 |
|
195 |
public function parseTitle() |
196 |
{
|
197 |
$newContent = str_replace('<title>', '<title>'. $page->getTitle(), $this->page->getContent() ); |
198 |
$this->page->setContent( $newContent ); |
199 |
}
|
200 |
|
201 |
/**
|
202 |
* Parse the page object into some output
|
203 |
* @return void
|
204 |
*/
|
205 |
public function parseOutput() |
206 |
{
|
207 |
$this->replaceBits(); |
208 |
$this->replaceTags(); |
209 |
$this->parseTitle(); |
210 |
}
|
211 |
|
212 |
|
213 |
|
214 |
}
|
215 |
?>
|
Kung kaya, ano ang eksaktong ginagawa ng class na ito?
Ginagawa ang ating page object, at binabatay ito sa template files, ang page object ay naglalaman ng content at impormasyon na kailangan para sa magiging HTML ng page. Kung kaya gumagawa tayo ng buildFromTemplate('templatefile.tpl.php', 'templatefile2.tpl.php') para makuha ang panimulang content para sa ating page, ang paraang ito ay kinukuha ang kahit na anong bilang ng template files bilang argument nito, at inaayos uli ang mga ito, mahalaga para sa header, content at footer templates.
Nagma-manage ng content na may kinalaman sa page sa pamamagitan ng pagtulong sa page object na mapanatili ang record ng data na papalitan sa page, at saka karagdagang template bits na kailangang isama sa page (addTemplateBit('userbar','usertoolsbar.tpl.php')).
Naglalagay ng data at content sa page sa pamamagitan ng paggawa ng iba’t ibang replace operations sa page content, kasama ang pagre-retrieve ng mga resulta mula sa cached query at paglagay ng mga ito sa page.
Ang template file ay kailangang maglagay ng marka mismo sa loob nito kung saan ang cached query ay kailangang i-retrieve at ang data mula sa query ay kailangang mapalitan. Kapag ang template manager ay nakahanap ng tag na papalitan na isang query, kinukuha nito ang chunk ng page kung saan kailangan nitong ulitin sa pamamagitan ng pagtawag sa getBlock(‘block’) sa page object. Ang chunk ng content na ito ay kinokopya sa bawat record sa query, at may tags sa loob nito na napalitan ng mga resulta mula sa query. Titingnan natin kung paano ang itsura nito sa template mamaya sa pagtuturong ito.
Ang Template Manager: Pahina
Ang page object ay mina-manage ng template manager, at ginagamit ito para hawakan lahat ng mga detalye na may kaugnayan sa page. Dahil dito ay malaya na nama-manage ito ng template manager, habang ginagawang madali nito para sa atin ang pahabain ang functionality nito sa hinaharap.
1 |
|
2 |
<?php
|
3 |
|
4 |
/**
|
5 |
* This is our page object
|
6 |
* It is a seperate object to allow some interesting extra functionality to be added
|
7 |
* Some ideas: passwording pages, adding page specific css/js files, etc
|
8 |
*/
|
9 |
class page { |
10 |
|
11 |
// room to grow later?
|
12 |
private $css = array(); |
13 |
private $js = array(); |
14 |
private $bodyTag = ''; |
15 |
private $bodyTagInsert = ''; |
16 |
|
17 |
// future functionality?
|
18 |
private $authorised = true; |
19 |
private $password = ''; |
20 |
|
21 |
// page elements
|
22 |
private $title = ''; |
23 |
private $tags = array(); |
24 |
private $postParseTags = array(); |
25 |
private $bits = array(); |
26 |
private $content = ""; |
27 |
|
28 |
/**
|
29 |
* Constructor...
|
30 |
*/
|
31 |
function __construct() { } |
32 |
|
33 |
public function getTitle() |
34 |
{
|
35 |
return $this->title; |
36 |
}
|
37 |
|
38 |
public function setPassword( $password ) |
39 |
{
|
40 |
$this->password = $password; |
41 |
}
|
42 |
|
43 |
public function setTitle( $title ) |
44 |
{
|
45 |
$this->title = $title; |
46 |
}
|
47 |
|
48 |
public function setContent( $content ) |
49 |
{
|
50 |
$this->content = $content; |
51 |
}
|
52 |
|
53 |
public function addTag( $key, $data ) |
54 |
{
|
55 |
$this->tags[$key] = $data; |
56 |
}
|
57 |
|
58 |
public function getTags() |
59 |
{
|
60 |
return $this->tags; |
61 |
}
|
62 |
|
63 |
public function addPPTag( $key, $data ) |
64 |
{
|
65 |
$this->postParseTags[$key] = $data; |
66 |
}
|
67 |
|
68 |
/**
|
69 |
* Get tags to be parsed after the first batch have been parsed
|
70 |
* @return array
|
71 |
*/
|
72 |
public function getPPTags() |
73 |
{
|
74 |
return $this->postParseTags; |
75 |
}
|
76 |
|
77 |
/**
|
78 |
* Add a template bit to the page, doesnt actually add the content just yet
|
79 |
* @param String the tag where the template is added
|
80 |
* @param String the template file name
|
81 |
* @return void
|
82 |
*/
|
83 |
public function addTemplateBit( $tag, $bit ) |
84 |
{
|
85 |
$this->bits[ $tag ] = $bit; |
86 |
}
|
87 |
|
88 |
/**
|
89 |
* Get the template bits to be entered into the page
|
90 |
* @return array the array of template tags and template file names
|
91 |
*/
|
92 |
public function getBits() |
93 |
{
|
94 |
return $this->bits; |
95 |
}
|
96 |
|
97 |
/**
|
98 |
* Gets a chunk of page content
|
99 |
* @param String the tag wrapping the block ( <!-- START tag --> block <!-- END tag --> )
|
100 |
* @return String the block of content
|
101 |
*/
|
102 |
public function getBlock( $tag ) |
103 |
{
|
104 |
preg_match ('#<!-- START '. $tag . ' -->(.+?)<!-- END '. $tag . ' -->#si', $this->content, $tor); |
105 |
|
106 |
$tor = str_replace ('<!-- START '. $tag . ' -->', "", $tor[0]); |
107 |
$tor = str_replace ('<!-- END ' . $tag . ' -->', "", $tor); |
108 |
|
109 |
return $tor; |
110 |
}
|
111 |
|
112 |
public function getContent() |
113 |
{
|
114 |
return $this->content; |
115 |
}
|
116 |
|
117 |
}
|
118 |
?>
|
Paano ang class na ito mas mapapahaba at mapapaganda?
- Maaaring gusto mong mapalitan ang tags pagkatapos na karamihan sa page ay ma-parse, marahil ang content sa database ay naglalaman ng tags na kailangang i-parse.
- Pahina na may password. Maglagay ng password sa page, tingnan kung ang user ay may password sa cookie o session kung saan maaari nilang makita ang page.
- Limitadong pages (bagaman ay kailagan muna natin ng authentication components!)
- Pagpapalit ng
- Paglalagay ng references sa javascript at css files batay sa page o application.
Naglo-load ng core objects
Ngayon na mayroon na tayong ilang objects kung saan ang ating registry ay ii-store para sa atin, kailangan nating sabihin sa regisgry kung aling objects ang mga ito. Gumawa ako ng method sa PCARegistry object na tinatawag na loadCoreObjects kung saan (gaya ng sinasabi nito) ay nilo-load ang core objects. Ibig sabihin nito maaari natin itong tawagin mula sa ating index.php file para i-load ang registry sa mga objects na ito.
1 |
|
2 |
public function storeCoreObjects() |
3 |
{
|
4 |
$this->storeObject('database', 'db' ); |
5 |
$this->storeObject('template', 'template' ); |
6 |
}
|
Ang method na ito ay maaaring mapalitan sa susunod para isama ang ilang pangunahing objects na kailangang i-load ng registry, siyempre maaaring mayroong objects na gusto nating i-manage ng ating registry, subalit depende sa application kung saan ang framework ay ginamit. Ang objects na ito ay ilo-load sa labas ng method na ito.
Ilang Data
Para maipakita natin ang bagong features na nadagdag sa ating framework, kailangan natin ng database para magamit natin ang database handler, at ilang template management functions(kung saan natin papalitan ang block ng content sa hanay sa database).
Ang demonstration site na gagawin natin sa ating framework sa dulo ng serye ng pagtuturong ito ay isang website na may directory ng mga miyembro, kung kaya gumawa tayo ng napaka basic na database table para sa profile ng mga miyembro, na naglalaman ng ID, pangalan, at email address.



Halata naman, kailangan natin ng ilang hanay ng data sa table na ito!
Mabilis na template
Para lahat ay maipakita, kailangan natin ng basic template, kung saan ililista natin ang data mula sa ating members table.
1 |
|
2 |
<html>
|
3 |
<head>
|
4 |
<title> Powered by PCA Framework</title> |
5 |
</head>
|
6 |
<body>
|
7 |
<h1>Our Members</h1> |
8 |
<p>Below is a list of our members:</p> |
9 |
<ul>
|
10 |
<!-- START members -->
|
11 |
<li>{name} {email}</li> |
12 |
<!-- END members -->
|
13 |
</ul>
|
14 |
</body>
|
15 |
</html>
|
Ang START members at END members HTML comments ay tumutukoy sa members block(kung saan ay nakukuha sa pamamagitan ng getBlock()method sa page), dito ay kung saan ang template manager ay mauulit sa records sa database at ipapakita ang mga ito.
Ginagamit na Framework
Ngayon, kailangan nating pagsama-samahin ang lahat ng ito, sa ating index.php file:
1 |
|
2 |
// require our registry
|
3 |
require_once('PCARegistry/pcaregistry.class.php'); |
4 |
$registry = PCARegistry::singleton(); |
5 |
|
6 |
// store those core objects
|
7 |
$registry->storeCoreObjects(); |
8 |
|
9 |
// create a database connection
|
10 |
$registry->getObject('db')->newConnection('localhost', 'root', '', 'pcaframework'); |
11 |
|
12 |
// set the default skin setting (we will store these in the database later...)
|
13 |
$registry->storeSetting('default', 'skin'); |
14 |
|
15 |
// populate our page object from a template file
|
16 |
$registry->getObject('template')->buildFromTemplates('main.tpl.php'); |
17 |
|
18 |
// cache a query of our members table
|
19 |
$cache = $registry->getObject('db')->cacheQuery('SELECT * FROM members'); |
20 |
|
21 |
// assign this to the members tag
|
22 |
$registry->getObject('template')->getPage()->addTag('members', array('SQL', $cache) ); |
23 |
|
24 |
// set the page title
|
25 |
$registry->getObject('template')->getPage()->setTitle('Our members'); |
26 |
|
27 |
// parse it all, and spit it out
|
28 |
$registry->getObject('template')->parseOutput(); |
29 |
print $registry->getObject('template')->getPage()->getContent(); |
Kung titingnan na natin ang page na ito sa ating web browser, ang mga resulta ng query ay ipinakita sa pahina:

Parating sa ikatlong bahagi…
Sa ikatlong bahagi ay medyo lilihis tayo mula sa development na panig ng ating Framework at tingnan kung paano mag-disenyo gamit ang framework na nasa isip natin, at kung paano hahatiin ang HTML templates para umakma sila sa ating framework. Kapag magsisimula pa lang tayong gumawa ng ating unang application sa framework, titingnan natin ng mas detalyado ang ilan sa mga ginagawa ng mga classes na ito. At ang panghuli, salamat sa iyong mga komento noong nakaraan!
- Mag-subscribe sa NETTUTS RSS Feed para sa mas marami pang tutoryal at artikulo tungkol sa web development.