() translation by (you can also view the original English article)
हमारे फ्रेमवर्क के लिए बेसिक स्ट्रक्चर के साथ, यह समय फंक्शनलिटी जोड़ना शुरू करने का है। इस ट्यूटोरियल में हम एक टेम्पलेट मैनेजर और डेटाबेस हैंडलर बनाएंगे, जिससे हमें लगभग किसी भी प्रोजेक्ट में उपयोग के लिए एक शक्तिशाली फ्रेमवर्क के करीब एक कदम मिल जाएगा। यदि आपने पहले से नहीं किया हैं, तो पहले इस सीरीज के भाग 1 की समीक्षा करना सुनिश्चित करें!
MVC: स्ट्रक्चर को ट्विक करें
इस ट्यूटोरियल के पहले भाग में, हमने अपने ऍप्लिकेशन्स के लिए बिज़नेस लॉजिक को स्टोर करने के लिए controllers नामक एक फ़ोल्डर बनाया है। जैसा कि daok ने एक कमेंट में बताया, यह सभी बिज़नेस लॉजिक के लिए सबसे अच्छी जगह नहीं है, और इस लॉजिक को स्टोर करने के लिए model का उपयोग किया जाना चाहिए। पहले, मैंने हमेशा अपने अधिकांश ऍप्लिकेशन्स में मॉडल के रूप में डेटाबेस का उपयोग किया है, हालांकि, इसे थोड़ा और अलग करने से हमारा फ्रेमवर्क और भी शक्तिशाली और एक्सटेंड करना आसान हो जाएगा।
तो, MVC क्या है? MVC एक डिजाइन पैटर्न है (जैसा कि सिंगलटन और रजिस्ट्री पैटर्न था जिसे हमने भाग 1 में देखा था), और यह मॉडल व्यू कंट्रोलर के लिए है, और इस पैटर्न का उद्देश्य बिज़नेस लॉजिक, यूजर इंटरफ़ेस एक्शन्स और यूजर इंटरफ़ेस को अलग करना है एक दूसरे से। यद्यपि हम अभी तक हमारे मॉडल और कंट्रोलर्स के साथ कुछ भी नहीं करने जा रहे हैं, आइए "models" फ़ोल्डर को शामिल करने के लिए हमारे फ्रेमवर्क फ़ोल्डर स्ट्रक्चर को अपडेट करें। मॉडल में मुख्य बिज़नेस लॉजिक होगा, और कंट्रोलर यूजर इंटरैक्शन से निपटेंगे (उदाहरण के लिए डेटा सबमिट करना, जैसे कोई कमेंट)। NB: हमारे __autoload फ़ंक्शन को बदलने की आवश्यकता नहीं है।



डेटाबेस हैंडलर
PHP का उपयोग करने वाली अधिकांश वेबसाइटें और वेब एप्लिकेशन भी डेटाबेस इंजन का उपयोग करते हैं, जैसे MySQL। यदि हम अपने सभी डेटाबेस से संबंधित फंक्शन्स को एक ही स्थान पर रखते हैं, तो हम (सैद्धांतिक रूप में) डेटाबेस इंजन को आसानी से बदल सकते हैं। हम कुछ ख़ास ऑपरेशन को आसान बना सकते हैं, जैसे रिकॉर्ड डालना, रिकॉर्ड अपडेट करना या डेटाबेस से रिकॉर्ड्स हटाना। यह कई डेटाबेस कनेक्शन से निपटने के दौरान भी आसान बना सकता है।
तो...हमारे डेटाबेस हैंडलर को क्या करना चाहिए:
- डेटाबेस में कनेक्शन मैनेज करें
- डेटाबेस से कुछ लेवल का अब्स्ट्रक्शन प्रदान करने का प्रयास करें
- क्वेरी को Cache करें ताकि हम बाद में उनका उपयोग कर सकें
- सामान्य डेटाबेस ऑपरेशन को आसान बनाएं
आइए हमारे डेटाबेस हैंडलर के लिए कोड देखें, फिर हम इसके बाद चर्चा करेंगे।
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
* Database management and access class
|
5 |
* This is a very basic level of abstraction
|
6 |
*/
|
7 |
class database { |
8 |
|
9 |
/**
|
10 |
* Allows multiple database connections
|
11 |
* probably not used very often by many applications, but still useful
|
12 |
*/
|
13 |
private $connections = array(); |
14 |
|
15 |
/**
|
16 |
* Tells the DB object which connection to use
|
17 |
* setActiveConnection($id) allows us to change this
|
18 |
*/
|
19 |
private $activeConnection = 0; |
20 |
|
21 |
/**
|
22 |
* Queries which have been executed and then "saved for later"
|
23 |
*/
|
24 |
private $queryCache = array(); |
25 |
|
26 |
/**
|
27 |
* Data which has been prepared and then "saved for later"
|
28 |
*/
|
29 |
private $dataCache = array(); |
30 |
|
31 |
/**
|
32 |
* Record of the last query
|
33 |
*/
|
34 |
private $last; |
35 |
|
36 |
|
37 |
/**
|
38 |
* Hello
|
39 |
*/
|
40 |
public function __construct() |
41 |
{
|
42 |
|
43 |
}
|
44 |
|
45 |
/**
|
46 |
* Create a new database connection
|
47 |
* @param String database hostname
|
48 |
* @param String database username
|
49 |
* @param String database password
|
50 |
* @param String database we are using
|
51 |
* @return int the id of the new connection
|
52 |
*/
|
53 |
public function newConnection( $host, $user, $password, $database ) |
54 |
{
|
55 |
$this->connections[] = new mysqli( $host, $user, $password, $database ); |
56 |
$connection_id = count( $this->connections )-1; |
57 |
if( mysqli_connect_errno() ) |
58 |
{
|
59 |
trigger_error('Error connecting to host. '.$this->connections[$connection_id]->error, E_USER_ERROR); |
60 |
}
|
61 |
|
62 |
return $connection_id; |
63 |
}
|
64 |
|
65 |
/**
|
66 |
* Close the active connection
|
67 |
* @return void
|
68 |
*/
|
69 |
public function closeConnection() |
70 |
{
|
71 |
$this->connections[$this->activeConnection]->close(); |
72 |
}
|
73 |
|
74 |
/**
|
75 |
* Change which database connection is actively used for the next operation
|
76 |
* @param int the new connection id
|
77 |
* @return void
|
78 |
*/
|
79 |
public function setActiveConnection( int $new ) |
80 |
{
|
81 |
$this->activeConnection = $new; |
82 |
}
|
83 |
|
84 |
/**
|
85 |
* Store a query in the query cache for processing later
|
86 |
* @param String the query string
|
87 |
* @return the pointed to the query in the cache
|
88 |
*/
|
89 |
public function cacheQuery( $queryStr ) |
90 |
{
|
91 |
if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) ) |
92 |
{
|
93 |
trigger_error('Error executing and caching query: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR); |
94 |
return -1; |
95 |
}
|
96 |
else
|
97 |
{
|
98 |
$this->queryCache[] = $result; |
99 |
return count($this->queryCache)-1; |
100 |
}
|
101 |
}
|
102 |
|
103 |
/**
|
104 |
* Get the number of rows from the cache
|
105 |
* @param int the query cache pointer
|
106 |
* @return int the number of rows
|
107 |
*/
|
108 |
public function numRowsFromCache( $cache_id ) |
109 |
{
|
110 |
return $this->queryCache[$cache_id]->num_rows; |
111 |
}
|
112 |
|
113 |
/**
|
114 |
* Get the rows from a cached query
|
115 |
* @param int the query cache pointer
|
116 |
* @return array the row
|
117 |
*/
|
118 |
public function resultsFromCache( $cache_id ) |
119 |
{
|
120 |
return $this->queryCache[$cache_id]->fetch_array(MYSQLI_ASSOC); |
121 |
}
|
122 |
|
123 |
/**
|
124 |
* Store some data in a cache for later
|
125 |
* @param array the data
|
126 |
* @return int the pointed to the array in the data cache
|
127 |
*/
|
128 |
public function cacheData( $data ) |
129 |
{
|
130 |
$this->dataCache[] = $data; |
131 |
return count( $this->dataCache )-1; |
132 |
}
|
133 |
|
134 |
/**
|
135 |
* Get data from the data cache
|
136 |
* @param int data cache pointed
|
137 |
* @return array the data
|
138 |
*/
|
139 |
public function dataFromCache( $cache_id ) |
140 |
{
|
141 |
return $this->dataCache[$cache_id]; |
142 |
}
|
143 |
|
144 |
/**
|
145 |
* Delete records from the database
|
146 |
* @param String the table to remove rows from
|
147 |
* @param String the condition for which rows are to be removed
|
148 |
* @param int the number of rows to be removed
|
149 |
* @return void
|
150 |
*/
|
151 |
public function deleteRecords( $table, $condition, $limit ) |
152 |
{
|
153 |
$limit = ( $limit == '' ) ? '' : ' LIMIT ' . $limit; |
154 |
$delete = "DELETE FROM {$table} WHERE {$condition} {$limit}"; |
155 |
$this->executeQuery( $delete ); |
156 |
}
|
157 |
|
158 |
/**
|
159 |
* Update records in the database
|
160 |
* @param String the table
|
161 |
* @param array of changes field => value
|
162 |
* @param String the condition
|
163 |
* @return bool
|
164 |
*/
|
165 |
public function updateRecords( $table, $changes, $condition ) |
166 |
{
|
167 |
$update = "UPDATE " . $table . " SET "; |
168 |
foreach( $changes as $field => $value ) |
169 |
{
|
170 |
$update .= "`" . $field . "`='{$value}',"; |
171 |
}
|
172 |
|
173 |
// remove our trailing ,
|
174 |
$update = substr($update, 0, -1); |
175 |
if( $condition != '' ) |
176 |
{
|
177 |
$update .= "WHERE " . $condition; |
178 |
}
|
179 |
|
180 |
$this->executeQuery( $update ); |
181 |
|
182 |
return true; |
183 |
|
184 |
}
|
185 |
|
186 |
/**
|
187 |
* Insert records into the database
|
188 |
* @param String the database table
|
189 |
* @param array data to insert field => value
|
190 |
* @return bool
|
191 |
*/
|
192 |
public function insertRecords( $table, $data ) |
193 |
{
|
194 |
// setup some variables for fields and values
|
195 |
$fields = ""; |
196 |
$values = ""; |
197 |
|
198 |
// populate them
|
199 |
foreach ($data as $f => $v) |
200 |
{
|
201 |
|
202 |
$fields .= "`$f`,"; |
203 |
$values .= ( is_numeric( $v ) && ( intval( $v ) == $v ) ) ? $v."," : "'$v',"; |
204 |
|
205 |
}
|
206 |
|
207 |
// remove our trailing ,
|
208 |
$fields = substr($fields, 0, -1); |
209 |
// remove our trailing ,
|
210 |
$values = substr($values, 0, -1); |
211 |
|
212 |
$insert = "INSERT INTO $table ({$fields}) VALUES({$values})"; |
213 |
$this->executeQuery( $insert ); |
214 |
return true; |
215 |
}
|
216 |
|
217 |
/**
|
218 |
* Execute a query string
|
219 |
* @param String the query
|
220 |
* @return void
|
221 |
*/
|
222 |
public function executeQuery( $queryStr ) |
223 |
{
|
224 |
if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) ) |
225 |
{
|
226 |
trigger_error('Error executing query: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR); |
227 |
}
|
228 |
else
|
229 |
{
|
230 |
$this->last = $result; |
231 |
}
|
232 |
|
233 |
}
|
234 |
|
235 |
/**
|
236 |
* Get the rows from the most recently executed query, excluding cached queries
|
237 |
* @return array
|
238 |
*/
|
239 |
public function getRows() |
240 |
{
|
241 |
return $this->last->fetch_array(MYSQLI_ASSOC); |
242 |
}
|
243 |
|
244 |
/**
|
245 |
* Gets the number of affected rows from the previous query
|
246 |
* @return int the number of affected rows
|
247 |
*/
|
248 |
public function affectedRows() |
249 |
{
|
250 |
return $this->$this->connections[$this->activeConnection]->affected_rows; |
251 |
}
|
252 |
|
253 |
/**
|
254 |
* Sanitize data
|
255 |
* @param String the data to be sanitized
|
256 |
* @return String the sanitized data
|
257 |
*/
|
258 |
public function sanitizeData( $data ) |
259 |
{
|
260 |
return $this->connections[$this->activeConnection]->real_escape_string( $data ); |
261 |
}
|
262 |
|
263 |
/**
|
264 |
* Deconstruct the object
|
265 |
* close all of the database connections
|
266 |
*/
|
267 |
public function __deconstruct() |
268 |
{
|
269 |
foreach( $this->connections as $connection ) |
270 |
{
|
271 |
$connection->close(); |
272 |
}
|
273 |
}
|
274 |
}
|
275 |
?>
|
इसे अधिक विस्तार से चर्चा करने से पहले, मुझे यह पॉइंट आउट करना चाहिए कि यह डेटाबेस हैंडलर बहुत बेसिक है। हम सीधे क्वेरी को एक्सेक्यूट नहीं करके पूर्ण अब्स्ट्रक्शन प्रदान कर सकते हैं, लेकिन इसके बजाय पैरामीटर पर आधारित क्वेरी फ़ंक्शन पर क्वेरी का निर्माण करना और फिर इसे एक्सेक्यूट करना।
हमारे डिलीट, इन्सर्ट और अपडेट मेथड्स कुछ सामान्य टास्कस को परफॉर्म करना आसान बनाता है (जैसा कि मैंने ऊपर उल्लेखित किया है, हम इसे और अधिक करने के लिए एक्सटेंड कर सकते हैं), केवल टेबल नाम, फ़ील्ड का एक array और करेस्पोंडिंग वैल्यू जैसी जानकारी प्रदान करके, वैल्यूज और कंडीशंस को सीमित करें। क्वेरी को "cached" भी किया जा सकता है ताकि हम बाद में उनके साथ चीजें कर सकें। मुझे यह सुविधा मिलती है (साथ ही डेटा के "cache" array की क्षमता) टेम्पलेट मैनेजर के साथ मिलकर बहुत आसान होती है, क्योंकि हम डेटा की row के माध्यम से आसानी से पुन: प्रयास कर सकते हैं और इसे अपने टेम्पलेट्स में थोड़ा झगड़ा कर सकते हैं, जैसा कि आप करेंगे देखें जब हम टेम्पलेट मैनेजर को देखते हैं।
1 |
// insert record
|
2 |
$registry->getObject('db')->insertRecords( 'testTable', array('name'=>'Michael' ) ); |
3 |
// update a record
|
4 |
$registry->getObject('db')->updateRecords( 'testTable', array('name'=>'MichaelP' ), 'ID=2' ); |
5 |
// delete a record (well, upto 5 in this case)
|
6 |
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 ); |
हम अपेक्षाकृत आसानी से कई डेटाबेस कनेक्शन के साथ भी काम कर सकते हैं, तब तक हम उचित कनेक्शन के बीच स्विच करते हैं, जब तक हमें आवश्यकता होती है (हालांकि इस क्वेरी के दौरान काम नहीं करेगा और बिना किसी काम के हमारे टेम्पलेट मैनेजर के माध्यम से उन्हें रिट्रीव करेगा), उदाहरण के लिए, नीचे कोड स्निपेट हमें दो डेटाबेस से रिकॉर्ड हटाने की अनुमति देगा।
1 |
// our second database connection (let's assume we already have a connection to our main DB)
|
2 |
$newConnection = $registry->getObject('db')->newConnection('localhost', 'root', 'password', 'secondDB'); |
3 |
// delete from the primary db connection
|
4 |
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 ); |
5 |
// change our active db connection, to allow future queries to be on the second connection
|
6 |
$registry->getObject('db')->setActiveConnection( $newConnection ); |
7 |
// delete from the secondary db connection
|
8 |
$registry->getObject('db')->deleteRecords( 'testTable', "name='MichaelP'", 5 ); |
9 |
// revert the active connection so future queries are on the primary db connection
|
10 |
$registry->getObject('db')->setActiveConnection( 0 ); |
हम इस क्लास को कैसे एक्सटेंड करना चाहते हैं?
- फुल अब्स्ट्रक्शन
- इनहेरिटेंस का उपयोग करें, एक इंटरफ़ेस बनाएं और डेटाबेस क्लास से इसका इन्हेरिट हो, हर डेटाबेस इंजन
- के लिए अलगक्वेरी को cache करते समय क्वेरी के साथ कनेक्शन id को स्टोर करें
- डेटा को सनिटीज़ (sanitize) करने के लिए इच्छित डेटा के प्रकार के आधार पर डेटा सनिटीज़िंग (sanitizing) में सुधार करें
टेम्पलेट मैनेजर
टेम्पलेट मैनेजर सभी आउटपुट को संभालेगा, इसे विभिन्न अलग-अलग टेम्पलेट फाइलों के साथ काम करने में सक्षम होना चाहिए, प्लेसहोल्डर को रीप्लेस करें (मैं उन्हें टैग कहता हूं) डेटा के साथ और डेटाबेस से डेटा की कई rows के साथ टेम्पलेट के हिस्सों के माध्यम से इटरेट करता हूं।
चीजों को आसान बनाने के लिए, हम पेज से संबंधित कंटेंट को शामिल करने के लिए page क्लास का उपयोग करेंगे, इससे हमारे लिए इसे एक्सटेंड करना और बाद में फीचर्स को जोड़ना आसान हो जाता है। टेम्पलेट मैनेजर इस ऑब्जेक्ट को मैनेज करेगा।
1 |
<?php
|
2 |
|
3 |
// prevent this file being called directly
|
4 |
if ( ! defined( 'PCAFW' ) ) |
5 |
{
|
6 |
echo 'This file can only be called via the main index.php file, and not directly'; |
7 |
exit(); |
8 |
}
|
9 |
|
10 |
/**
|
11 |
* Template manager class
|
12 |
*/
|
13 |
class template { |
14 |
|
15 |
private $page; |
16 |
|
17 |
/**
|
18 |
* Hello!
|
19 |
*/
|
20 |
public function __construct() |
21 |
{
|
22 |
include( APP_PATH . '/PCARegistry/objects/page.class.php'); |
23 |
$this->page = new Page(); |
24 |
|
25 |
}
|
26 |
|
27 |
/**
|
28 |
* Add a template bit onto our page
|
29 |
* @param String $tag the tag where we insert the template e.g. {hello}
|
30 |
* @param String $bit the template bit (path to file, or just the filename)
|
31 |
* @return void
|
32 |
*/
|
33 |
public function addTemplateBit( $tag, $bit ) |
34 |
{
|
35 |
if( strpos( $bit, 'skins/' ) === false ) |
36 |
{
|
37 |
$bit = 'skins/' . PCARegistry::getSetting('skin') . '/templates/' . $bit; |
38 |
}
|
39 |
$this->page->addTemplateBit( $tag, $bit ); |
40 |
}
|
41 |
|
42 |
/**
|
43 |
* Put the template bits into our page content
|
44 |
* Updates the pages content
|
45 |
* @return void
|
46 |
*/
|
47 |
private function replaceBits() |
48 |
{
|
49 |
$bits = $this->page->getBits(); |
50 |
foreach( $bits as $tag => $template ) |
51 |
{
|
52 |
$templateContent = file_get_contents( $bit ); |
53 |
$newContent = str_replace( '{' . $tag . '}', $templateContent, $this->page->getContent() ); |
54 |
$this->page->setContent( $newContent ); |
55 |
}
|
56 |
}
|
57 |
|
58 |
/**
|
59 |
* Replace tags in our page with content
|
60 |
* @return void
|
61 |
*/
|
62 |
private function replaceTags() |
63 |
{
|
64 |
// get the tags
|
65 |
$tags = $this->page->getTags(); |
66 |
// go through them all
|
67 |
foreach( $tags as $tag => $data ) |
68 |
{
|
69 |
if( is_array( $data ) ) |
70 |
{
|
71 |
|
72 |
if( $data[0] == 'SQL' ) |
73 |
{
|
74 |
// it is a cached query...replace DB tags
|
75 |
$this->replaceDBTags( $tag, $data[1] ); |
76 |
}
|
77 |
elseif( $data[0] == 'DATA' ) |
78 |
{
|
79 |
// it is some cached data...replace data tags
|
80 |
$this->replaceDataTags( $tag, $data[1] ); |
81 |
}
|
82 |
}
|
83 |
else
|
84 |
{
|
85 |
// replace the content
|
86 |
$newContent = str_replace( '{' . $tag . '}', $data, $this->page->getContent() ); |
87 |
// update the pages content
|
88 |
$this->page->setContent( $newContent ); |
89 |
}
|
90 |
}
|
91 |
}
|
92 |
|
93 |
/**
|
94 |
* Replace content on the page with data from the DB
|
95 |
* @param String $tag the tag defining the area of content
|
96 |
* @param int $cacheId the queries ID in the query cache
|
97 |
* @return void
|
98 |
*/
|
99 |
private function replaceDBTags( $tag, $cacheId ) |
100 |
{
|
101 |
$block = ''; |
102 |
$blockOld = $this->page->getBlock( $tag ); |
103 |
|
104 |
// foreach record relating to the query...
|
105 |
while ($tags = PCARegistry::getObject('db')->resultsFromCache( $cacheId ) ) |
106 |
{
|
107 |
$blockNew = $blockOld; |
108 |
// create a new block of content with the results replaced into it
|
109 |
foreach ($tags as $ntag => $data) |
110 |
{
|
111 |
$blockNew = str_replace("{" . $ntag . "}", $data, $blockNew); |
112 |
}
|
113 |
$block .= $blockNew; |
114 |
}
|
115 |
$pageContent = $this->page->getContent(); |
116 |
// remove the seperator in the template, cleaner HTML
|
117 |
$newContent = str_replace( '<!-- START ' . $tag . ' -->' . $blockOld . '<!-- END ' . $tag . ' -->', $block, $pageContent ); |
118 |
// update the page content
|
119 |
$this->page->setContent( $newContent ); |
120 |
}
|
121 |
|
122 |
/**
|
123 |
* Replace content on the page with data from the cache
|
124 |
* @param String $tag the tag defining the area of content
|
125 |
* @param int $cacheId the datas ID in the data cache
|
126 |
* @return void
|
127 |
*/
|
128 |
private function replaceDataTags( $tag, $cacheId ) |
129 |
{
|
130 |
$block = $this->page->getBlock( $tag ); |
131 |
$blockOld = $block; |
132 |
while ($tags = PCARegistry::getObject('db')->dataFromCache( $cacheId ) ) |
133 |
{
|
134 |
foreach ($tags as $tag => $data) |
135 |
{
|
136 |
$blockNew = $blockOld; |
137 |
$blockNew = str_replace("{" . $tag . "}", $data, $blockNew); |
138 |
}
|
139 |
$block .= $blockNew; |
140 |
}
|
141 |
$pageContent = $this->page->getContent(); |
142 |
$newContent = str_replace( $blockOld, $block, $pageContent ); |
143 |
$this->page->setContent( $newContent ); |
144 |
}
|
145 |
|
146 |
/**
|
147 |
* Get the page object
|
148 |
* @return Object
|
149 |
*/
|
150 |
public function getPage() |
151 |
{
|
152 |
return $this->page; |
153 |
}
|
154 |
|
155 |
/**
|
156 |
* Set the content of the page based on a number of templates
|
157 |
* pass template file locations as individual arguments
|
158 |
* @return void
|
159 |
*/
|
160 |
public function buildFromTemplates() |
161 |
{
|
162 |
$bits = func_get_args(); |
163 |
$content = ""; |
164 |
foreach( $bits as $bit ) |
165 |
{
|
166 |
|
167 |
if( strpos( $bit, 'skins/' ) === false ) |
168 |
{
|
169 |
$bit = 'skins/' . PCARegistry::getSetting('skin') . '/templates/' . $bit; |
170 |
}
|
171 |
if( file_exists( $bit ) == true ) |
172 |
{
|
173 |
$content .= file_get_contents( $bit ); |
174 |
}
|
175 |
|
176 |
}
|
177 |
$this->page->setContent( $content ); |
178 |
}
|
179 |
|
180 |
/**
|
181 |
* Convert an array of data (i.e. a db row?) to some tags
|
182 |
* @param array the data
|
183 |
* @param string a prefix which is added to field name to create the tag name
|
184 |
* @return void
|
185 |
*/
|
186 |
public function dataToTags( $data, $prefix ) |
187 |
{
|
188 |
foreach( $data as $key => $content ) |
189 |
{
|
190 |
$this->page->addTag( $key.$prefix, $content); |
191 |
}
|
192 |
}
|
193 |
|
194 |
public function parseTitle() |
195 |
{
|
196 |
$newContent = str_replace('<title>', '<title>'. $page->getTitle(), $this->page->getContent() ); |
197 |
$this->page->setContent( $newContent ); |
198 |
}
|
199 |
|
200 |
/**
|
201 |
* Parse the page object into some output
|
202 |
* @return void
|
203 |
*/
|
204 |
public function parseOutput() |
205 |
{
|
206 |
$this->replaceBits(); |
207 |
$this->replaceTags(); |
208 |
$this->parseTitle(); |
209 |
}
|
210 |
|
211 |
|
212 |
|
213 |
}
|
214 |
?>
|
तो, यह क्लास वास्तव में क्या करती है?
हमारा पेज ऑब्जेक्ट बनाता है, और इसे टेम्पलेट फ़ाइलों से बेस करता है, पेज ऑब्जेक्ट में कंटेंट और जानकारी होती है जो पेज के HTML को बनाने के लिए आवश्यक होती है। इसके बाद हम अपने पेज के लिए प्रारंभिक कंटेंट प्राप्त करने के लिए FromTemplate ('templatefile.tpl.php', 'templatefile.tpl.php', 'templatefile2.tpl.php') बनाते हैं, यह मेथड किसी भी टेम्पलेट फ़ाइलों को इसके आर्गुमेंट के रूप में लेती है, और इसके लिए उपयोगी होती है हैडर, कंटेंट और फुटर टेम्पलेट्स।
पेज ऑब्जेक्ट को पेज में रीप्लेस करने के लिए डेटा के रिकॉर्ड को बनाए रखने में मदद करके पेज से जुड़े कंटेंट को मैनेज करता है, और अतिरिक्त टेम्पलेट बिट्स जिन्हें पेज में शामिल करने की आवश्यकता होती है (addTemplateBit ('userbar', 'usertoolsbar.tpl.php '))।
पेज कंटेंट पर विभिन्न रीप्लेस ऑपरेशन करके पेज पर डेटा और कंटेंट जोड़ता है, जिसमें cache किए गए क्वेरी से परिणाम रिट्रीव करना और उन्हें पेज पर जोड़ना शामिल है।
टेम्पलेट फ़ाइल को अपने आप को मार्क करने की आवश्यकता है जहां एक cache किए गए क्वेरी को रिट्रीव करने की आवश्यकता होती है और क्वेरी से डेटा बदल दिया जाता है। जब टेम्पलेट मैनेजर एक टैग को रीप्लेस करने के लिए एक टैग से मुकाबला करता है, तो यह उस पेज का हिस्सा हो जाता है जहां उसे पेज ऑब्जेक्ट पर getBlock('block') को कॉल करके पुन: प्रयास करने की आवश्यकता होती है। कंटेंट के इस हिस्से को फिर क्वेरी में प्रत्येक रिकॉर्ड के लिए कॉपी किया जाता है, और उसके भीतर टैग को क्वेरी के परिणामों के साथ रीप्लेस किया जाता है। हम इस ट्यूटोरियल में बाद में टेम्पलेट कैसा दिखता हैं, इस पर एक नज़र डालेंगे।
टेम्पलेट मैनेजर: पेज
पेज ऑब्जेक्ट टेम्पलेट मैनेजर द्वारा मैनेज किया जाता है, और इसमें पेज से संबंधित सभी details शामिल होते हैं। यह टेम्पलेट मैनेजर को मैनेज करने के लिए स्वतंत्र छोड़ देता है, जबकि इसे बाद की तारीख में इसकी फंक्शनलिटी को एक्सटेंड करना आसान बनाता है।
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
* This is our page object
|
5 |
* It is a seperate object to allow some interesting extra functionality to be added
|
6 |
* Some ideas: passwording pages, adding page specific css/js files, etc
|
7 |
*/
|
8 |
class page { |
9 |
|
10 |
// room to grow later?
|
11 |
private $css = array(); |
12 |
private $js = array(); |
13 |
private $bodyTag = ''; |
14 |
private $bodyTagInsert = ''; |
15 |
|
16 |
// future functionality?
|
17 |
private $authorised = true; |
18 |
private $password = ''; |
19 |
|
20 |
// page elements
|
21 |
private $title = ''; |
22 |
private $tags = array(); |
23 |
private $postParseTags = array(); |
24 |
private $bits = array(); |
25 |
private $content = ""; |
26 |
|
27 |
/**
|
28 |
* Constructor...
|
29 |
*/
|
30 |
function __construct() { } |
31 |
|
32 |
public function getTitle() |
33 |
{
|
34 |
return $this->title; |
35 |
}
|
36 |
|
37 |
public function setPassword( $password ) |
38 |
{
|
39 |
$this->password = $password; |
40 |
}
|
41 |
|
42 |
public function setTitle( $title ) |
43 |
{
|
44 |
$this->title = $title; |
45 |
}
|
46 |
|
47 |
public function setContent( $content ) |
48 |
{
|
49 |
$this->content = $content; |
50 |
}
|
51 |
|
52 |
public function addTag( $key, $data ) |
53 |
{
|
54 |
$this->tags[$key] = $data; |
55 |
}
|
56 |
|
57 |
public function getTags() |
58 |
{
|
59 |
return $this->tags; |
60 |
}
|
61 |
|
62 |
public function addPPTag( $key, $data ) |
63 |
{
|
64 |
$this->postParseTags[$key] = $data; |
65 |
}
|
66 |
|
67 |
/**
|
68 |
* Get tags to be parsed after the first batch have been parsed
|
69 |
* @return array
|
70 |
*/
|
71 |
public function getPPTags() |
72 |
{
|
73 |
return $this->postParseTags; |
74 |
}
|
75 |
|
76 |
/**
|
77 |
* Add a template bit to the page, doesnt actually add the content just yet
|
78 |
* @param String the tag where the template is added
|
79 |
* @param String the template file name
|
80 |
* @return void
|
81 |
*/
|
82 |
public function addTemplateBit( $tag, $bit ) |
83 |
{
|
84 |
$this->bits[ $tag ] = $bit; |
85 |
}
|
86 |
|
87 |
/**
|
88 |
* Get the template bits to be entered into the page
|
89 |
* @return array the array of template tags and template file names
|
90 |
*/
|
91 |
public function getBits() |
92 |
{
|
93 |
return $this->bits; |
94 |
}
|
95 |
|
96 |
/**
|
97 |
* Gets a chunk of page content
|
98 |
* @param String the tag wrapping the block ( <!-- START tag --> block <!-- END tag --> )
|
99 |
* @return String the block of content
|
100 |
*/
|
101 |
public function getBlock( $tag ) |
102 |
{
|
103 |
preg_match ('#<!-- START '. $tag . ' -->(.+?)<!-- END '. $tag . ' -->#si', $this->content, $tor); |
104 |
|
105 |
$tor = str_replace ('<!-- START '. $tag . ' -->', "", $tor[0]); |
106 |
$tor = str_replace ('<!-- END ' . $tag . ' -->', "", $tor); |
107 |
|
108 |
return $tor; |
109 |
}
|
110 |
|
111 |
public function getContent() |
112 |
{
|
113 |
return $this->content; |
114 |
}
|
115 |
|
116 |
}
|
117 |
?>
|
इस क्लास को कैसे एक्सटेंड किया जा सकता है और इसमें सुधार किया जा सकता है?
- PostParseTags: अधिकांश पेज के पार्स होने के बाद आप टैग बदलना चाहेंगे, शायद डेटाबेस के कंटेंट में ऐसे टैग होते हैं जिन्हें पार्स करने की आवश्यकता होती है।
- पासवर्ड वाले page: किसी पेज पर पासवर्ड असाइन करें, यह देखने के लिए जांचें कि यूजर के पास कुकी या सेशन में पासवर्ड है या नहीं, ताकि उन्हें पेज देखने की अनुमति मिल सके।
- रिस्ट्रिक्टेड पेज बदलना (हालांकि हमें पहले हमारे ऑथेंटिकेशन कंपोनेंट्स की आवश्यकता है!)
- पेज या एप्लिकेशन के
- आधार पर जावास्क्रिप्ट और CSS फ़ाइलों को डायनामिक रूप से रिफरेन्स जोड़ना।
कोर ऑब्जेक्ट्स को लोड करें
अब हमारे पास कुछ ऑब्जेक्ट्स हैं जो हमारी रजिस्ट्री हमारे लिए स्टोर करने जा रहे हैं, हमें इन ऑब्जेक्ट्स के बारे में रजिस्ट्री को बताने की ज़रूरत है। मैंने PCARegistry ऑब्जेक्ट में एक मेथड बनाया है जिसे loadCoreObjects कहा जाता है यह (जैसा कि यह कहता है) कोर ऑब्जेक्ट्स को लोड करता है। इसका मतलब यह है कि इन ऑब्जेक्ट्स के साथ रजिस्ट्री को लोड करने के लिए बस इससे हमारी index.php फ़ाइल से कॉल कर सकते हैं।
1 |
public function storeCoreObjects() |
2 |
{
|
3 |
$this->storeObject('database', 'db' ); |
4 |
$this->storeObject('template', 'template' ); |
5 |
}
|
रजिस्ट्री को लोड करने वाले अन्य कोर ऑब्जेक्ट्स को शामिल करने के लिए बाद में इस मेथड को बदला जा सकता है, निश्चित रूप से ऐसे ऑब्जेक्ट्स हो सकते हैं जिनसे हम अपनी रजिस्ट्री को मैनेज करना चाहते हैं, लेकिन केवल फ्रेमवर्क के लिए उपयोग किए जाने वाले एप्लिकेशन के आधार पर। इन ऑब्जेक्ट्स को इस मेथड के बाहर लोड किया जाएगा।
कुछ डाटा
ताकि हम अपने फ्रेमवर्क में जोड़े गए नए फीचर्स का प्रदर्शन कर सकें, हमें डेटाबेस हैंडलर का उपयोग करने के लिए डेटाबेस की आवश्यकता है, और कुछ टेम्पलेट मैनेजमेंट फ़ंक्शन (जहां हम डेटाबेस में rows के साथ कंटेंट के ब्लॉक को रीप्लेस करते हैं)।
डेमोंस्ट्रेशन साइट जो हम ट्यूटोरियल्स की इस सीरीज के अंत तक हमारे फ्रेमवर्क के साथ करेंगे, एक मेम्बर्स डायरेक्टरी वाली वेबसाइट है, इसलिए चलिए मेंबर प्रोफाइल के लिए एक बहुत ही बेसिक डेटाबेस टेबल बनाते हैं, जिसमें एक id, name और email एड्रेस होता है।



जाहिर है, हमें इस टेबल में डाटा की कुछ rows की आवश्यकता है!
एक क्विक टेम्पलेट
कुछ भी प्रदर्शित होने के लिए, हमें एक बेसिक टेम्पलेट की आवश्यकता है, जहां हम अपने मेंबर्स की टेबल से डाटा लिस्ट करेंगे।
1 |
<html>
|
2 |
<head>
|
3 |
<title> Powered by PCA Framework</title> |
4 |
</head>
|
5 |
<body>
|
6 |
<h1>Our Members</h1> |
7 |
<p>Below is a list of our members:</p> |
8 |
<ul>
|
9 |
<!-- START members -->
|
10 |
<li>{name} {email}</li> |
11 |
<!-- END members -->
|
12 |
</ul>
|
13 |
</body>
|
14 |
</html>
|
START मेंबर और END मेंबर HTML कमैंट्स मेंबर ब्लॉक को डेनोट करती हैं (जो पेज पर getBlock() मेथड के माध्यम से प्राप्त की जाती है), यह वह जगह है जहां टेम्पलेट मैनेजर डेटाबेस में रिकॉर्ड्स के माध्यम से पुन: प्रयास करेगा और उन्हें प्रदर्शित करेगा।
उपयोग करते हुए फ्रेमवर्क
अब, हमें यह सब एक साथ लाने की जरूरत है, हमारी index.php फ़ाइल के साथ:
1 |
// require our registry
|
2 |
require_once('PCARegistry/pcaregistry.class.php'); |
3 |
$registry = PCARegistry::singleton(); |
4 |
|
5 |
// store those core objects
|
6 |
$registry->storeCoreObjects(); |
7 |
|
8 |
// create a database connection
|
9 |
$registry->getObject('db')->newConnection('localhost', 'root', '', 'pcaframework'); |
10 |
|
11 |
// set the default skin setting (we will store these in the database later...)
|
12 |
$registry->storeSetting('default', 'skin'); |
13 |
|
14 |
// populate our page object from a template file
|
15 |
$registry->getObject('template')->buildFromTemplates('main.tpl.php'); |
16 |
|
17 |
// cache a query of our members table
|
18 |
$cache = $registry->getObject('db')->cacheQuery('SELECT * FROM members'); |
19 |
|
20 |
// assign this to the members tag
|
21 |
$registry->getObject('template')->getPage()->addTag('members', array('SQL', $cache) ); |
22 |
|
23 |
// set the page title
|
24 |
$registry->getObject('template')->getPage()->setTitle('Our members'); |
25 |
|
26 |
// parse it all, and spit it out
|
27 |
$registry->getObject('template')->parseOutput(); |
28 |
print $registry->getObject('template')->getPage()->getContent(); |
अगर हम अब इस पेज को हमारे वेब ब्राउज़र में देखते हैं, तो क्वेरी के परिणाम पेज पर डिस्प्ले होते हैं:

भाग 3 में आ रहा है...
भाग तीन में हम अपने फ्रेमवर्क के डेवलपमेंट साइड से थोड़ी सी चक्कर लगाएंगे, और देखेंगे कि हमारे फ्रेमवर्क के साथ कैसे डिजाइन किया जाए, और HTML टेम्पलेट्स को कैसे स्लाइस किया जाए ताकि वे हमारे फ्रेमवर्क के लिए उपयुक्त हों। जब हम अपने फ्रेमवर्क के साथ अपना पहला एप्लीकेशन बनाना शुरू करते हैं, तो हम इन क्लासेज के कुछ फंक्शन्स में अधिक जानकारी देखेंगे। अंत में, पिछली बार आपके कमैंट्स के लिए धन्यवाद!
- अधिक डेली वेब डेवलपमेंट टुट्स और आर्टिकल्स के लिए NETTUTS RSS Feed की सदस्यता लें।