Tải bản đầy đủ (.pdf) (10 trang)

Thiết kế mạng xã hội với PHP - 6 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.63 MB, 10 trang )

Planning and Developing the Core Framework
[ 32 ]
Connecting to the database and managing connections
In order to connect to multiple databases, we need to maintain a record of the
different connections. This can be done by storing each connection resource in
an array, keeping a record as to which of the items in this array is the active
connection. When a query is executed, it will perform the query against the
currently active connection.
<?php
/**
* Database management / access class: basic abstraction
*
* @author Michael Peacock
* @version 1.0
*/
class Mysqldb {

/**
* Allows multiple database connections
* each connection is stored as an element in the array, and the
active connection is maintained in a variable (see below)
*/
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 the results cached for


later, primarily for use within the template engine
*/
private $queryCache = array();

/**
* Data which has been prepared and then cached for later usage,
primarily within the template engine
*/
private $dataCache = array();

/**
* Number of queries made during execution process
*/
private $queryCounter = 0;
Download from Wow! eBook <www.wowebook.com>
Chapter 2
[ 33 ]

/**
* Record of the last query
*/
private $last;

/**
* Reference to the registry object
*/
private $registry;

/**
* Construct our database object

*/
public function __construct( Registry $registry )
{
$this->registry = $registry;
}
To connect to the database, we pass the database server host, username, and
password and of course the name of the database we wish to connect to. The
resulting connection is stored in our connections array, and the connection ID
(Array key) is returned.
/**
* 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;
}

Download from Wow! eBook <www.wowebook.com>
Planning and Developing the Core Framework
[ 34 ]
When we need to swap between connections, for example, to look up data from
an external source, or authenticate against another system, we need to tell the
database object to use this different connection. This is achieved through the
setActiveConnection method.
/**
* 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;
}
Executing queries
After a query is executed, we may wish to get the rows from the result of the query;
to allow us to do this, we simply store the result of the query in the classes $last
variable, so that it can be accessed by other methods.
/**
* 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: ' . $queryStr .' -
'.$this->connections[$this->activeConnection]->error,
E_USER_ERROR);
}
else
{
$this->last = $result;
}

}
Download from Wow! eBook <www.wowebook.com>
Chapter 2
[ 35 ]
When we do need to get the results from a query, we simply call the MySQLi fetch_
array
method on the result stored in the last variable.
/**
* Get the rows from the most recently executed query, excluding
cached queries
* @return array
*/
public function getRows()
{
return $this->last->fetch_array(MYSQLI_ASSOC);
}
Simplifying common queries
Common queries such as INSERT, UPDATE, and DELETE are often very repetitive;
however, they are quite easy to abstract the basics of into our database management
class. This won't work for all situations, but should make our lives easier for the

bulk of these operations. We can abstract select queries to this class too. However,
these are much more complicated, particularly, as we will more often than not, need
to utilize more complicated logic, such as sub-queries, joins, and aliases. This more
complicated logic would need to be developed into the code.
Deleting records can be done simply using the table name, conditions, and a limit. In
some cases, a limit may not be required, so if a non-empty string is passed, we need
to add the
LIMIT keyword to the query.
/**
* 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 == '' ) ? '' : ' LIMIT ' . $limit;
$delete = "DELETE FROM {$table} WHERE {$condition} {$limit}";
$this->executeQuery( $delete );
}
Download from Wow! eBook <www.wowebook.com>
Planning and Developing the Core Framework
[ 36 ]
Updating and inserting records are tasks I nd to be the most cumbersome; however,
we can easily abstract these simply by passing the table name, an array of eld
names and eld value pairs, and in the case of update operations, a condition.
/**
* 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 " . $table . " SET ";
foreach( $changes as $field => $value )
{
$update .= "`" . $field . "`='{$value}',";
}

// remove our trailing ,
$update = substr($update, 0, -1);
if( $condition != '' )
{
$update .= "WHERE " . $condition;
}
$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
Download from Wow! eBook <www.wowebook.com>
Chapter 2
[ 37 ]
foreach ($data as $f => $v)
{

$fields .= "`$f`,";
$values .= ( is_numeric( $v ) && ( intval( $v ) == $v ) ) ?
$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})";
//echo $insert;
$this->executeQuery( $insert );
return true;
}
Sanitizing data
Depending on the exact PHP setup, data needs to be sanitized slightly differently,

to prevent characters being escaped too many times. This is often the result of
magic_quotes_gpc setting. To make things easier, and to provide a single place
for changes to be made depending on our server's conguration, we can centralize
our data sanitization.
/**
* Sanitize data
* @param String the data to be sanitized
* @return String the sanitized data
*/
public function sanitizeData( $value )
{
// Stripslashes
if ( get_magic_quotes_gpc() )
{
$value = stripslashes ( $value );
}

// Quote value
if ( version_compare( phpversion(), "4.3.0" ) == "-1" )
{
$value = $this->connections[$this->activeConnection]-
Download from Wow! eBook <www.wowebook.com>
Planning and Developing the Core Framework
[ 38 ]
>escape_string( $value );
}
else
{
$value = $this->connections[$this->activeConnection]-
>real_escape_string( $value );

}
return $value;
}
Wrapping other MySQLi functions
This leaves us with a few other common MySQLi functions to wrap into our class,
including fetching the data from the executed query, fetching the number of rows
returned by a query, and getting the number of rows affected by a query.
/**
* Get the rows from the most recently executed query, excluding
cached queries
* @return array
*/
public function getRows()
{
return $this->last->fetch_array(MYSQLI_ASSOC);
}
public function numRows()
{
return $this->last->num_rows;
}
/**
* Gets the number of affected rows from the previous query
* @return int the number of affected rows
*/
public function affectedRows()
{
return $this->last->affected_rows;
}
Download from Wow! eBook <www.wowebook.com>
Chapter 2

[ 39 ]
Disconnecting
When the database object is no longer required, we should disconnect from all of the
connections we have made to various databases. This can be done through a simple
foreach loop in the deconstructor.
/**
* Deconstruct the object
* close all of the database connections
*/
public function __deconstruct()
{
foreach( $this->connections as $connection )
{
$connection->close();
}
}
}
?>
Template management
Template management is another set of core tasks that will need to be accessed by
almost every aspect of our social network code. Every page request needs to display
something to the user, and for each user the page will normally be different, and
contain dynamic data from our database.
For example, when any user views their friends list, they will all see the same page
layout; however, the list of friends will be different. When they view a prole, all
proles will have the same layout, with different data, and in some cases, some
additional sections to the page, depending on how complete their prole is.
Our template manager should take a series of template les, which contain the
HTML to be sent to the browser, and manage data, which should be inserted
into it, as well as process this dynamic replacement of data.

Additional templates should be able to be included within a template, should they
be required—for instance when viewing the prole of a user who has comments
enabled, a comments list and form should be displayed, whereas a user without this
would not see a list of a comments form.
The data and template contents will be stored in a Page object; the management of
this object and its processing will be handled by the template object. Let's go through
what we need in our template class (
registry/template.class.php).
Download from Wow! eBook <www.wowebook.com>
Planning and Developing the Core Framework
[ 40 ]
Firstly, we need to create the object, which involves assigning our registry to a
variable, including the page class, and instantiating a page object.
/**
* Include our page class, and build a page object to manage the
content and structure of the page
* @param Object our registry object
*/
public function __construct( Registry $registry )
{
$this->registry = $registry;
include( FRAMEWORK_PATH . '/registry/page.class.php');
$this->page = new Page( $this->registry );
}
Since the views are made up of a number of template les, we need to be able to
include these les and send them to our page object. Certain pages might be made
up of two templates, others may be made up of three or more. To make this exible,
instead of dening parameters for this method, we instead take however many
templates are passed as parameters and include them, in order, to our page object.
/**

* 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, 'views/' ) === false )
{
$bit = 'views/' . $this->registry->getSetting('view') . '/
templates/' . $bit;
}
if( file_exists( $bit ) == true )
{
$content .= file_get_contents( $bit );
}

}
$this->page->setContent( $content );
}
Download from Wow! eBook <www.wowebook.com>
Chapter 2
[ 41 ]
Within our template les, we may need to insert other templates. For instance, as
we mentioned earlier, if one user has comments enabled on their prole, and another
doesn't, then they will use the same main template, however, different templates will

be inserted dynamically into them.
We can do this by taking a
$tag (which is something contained within the template
already included), and a template $bit, which is included and placed within the
main template where the $tag was found.
/**
* Add a template bit from a view to our page
* @param String $tag the tag where we insert the template e.g.
{hello}
* @param String $bit the template bit (path to file, or just the
filename)
* @return void
*/
public function addTemplateBit( $tag, $bit )
{
if( strpos( $bit, 'views/' ) === false )
{
$bit = 'views/' . $this->registry->getSetting('view') . '/
templates/' . $bit;
}
$this->page->addTemplateBit( $tag, $bit );
}
These templates bits that we insert into our page object need to actually be replaced
into the current page, which is where the replaceBits method comes in. This iterates
through the list of template bits, and performs the replacement. The replacement is
done in order, so if we wanted to insert a template into a page, and then insert another
template into that one, we can do, so long as they were added in order.
The replacement is a simple
str_replace to nd the tag, and replace it with the
contents from the template.

/**
* Take the template bits from the view and insert them into our page
content
* Updates the pages content
* @return void
*/
private function replaceBits()
{
$bits = $this->page->getBits();
// loop through template bits
Download from Wow! eBook <www.wowebook.com>

×