"What? PHP? Are you crazy?" - You may say. We all know that there are more elegant, faster, less error-prone languages. I do love Python for elegance, Java for static typing, C++ for performance, however, please be aware of that, one can find faster good PHP programmer to hire than average C++ programmer who does understand at least importance of virtual destructor for avoiding memory leaks. In this series I'm going to show how can we adapt PHP (and it's most popular framework) for serious web-based systems with features I expect from Django/RoR/... environments. May be not easy, but let's try!
Installation
First of all, we need to download CakePHP source, we will use (the current stable version) 2.3.1:
$ cd /var/www
$ wget --no-check-certificate https://github.com/cakephp/cakephp/archive/2.3.1.tar.gz
$ tar zxf 2.3.1.tar.gz
Secondly, our document root must be redirected to app/webroot subdirectory of CakePHP installation directory (we use Apache 2.0, you have to use root account as it's a system-level service):
# vi /etc/apache2/sites-available/default
DocumentRoot /var/www/cakephp-2.3.1/app/webroot
/etc/init.d/apache2 restart
Then randomize Security.salt and Security.cipherSeed values in app/Config/core.php. Hint: you can use pwgen tool to create pretty random strings there:
$ pwgen -s 64 1
$ vi app/Config/core.php
Let's start with database configuration then: firstly, we adapt template of configuration to suit our database setup:
$ mv app/Config/database.php.default app/Config/database.php
$ pwgen -s 32 1
$ vi app/Config/database.php
Then create MySQL user and database:
$ echo "CREATE USER phpcake IDENTIFIED BY '<MYSQL_USER_PASS>'" | mysql -u root -p<MYSQL_ROOT_PASS>
$ echo "CREATE DATABASE phpcake" | mysql -u root -p<MYSQL_ROOT_PASS>
$ echo "GRANT ALL ON * TO phpcake" | mysql -u phpcake -p<MYSQL_USER_PASS> phpcake
You can verify if DB setup is correct by using default application installed in CakePHP: it will show you DB connection status:
Sample Model
In order to show very basic database functionality we will create two tables: one for payment representation, one for account, both connected by foreign key relation:
CREATE TABLE Account (id INT NOT NULL AUTO_INCREMENT, name VARCHAR(32), currency VARCHAR(3), PRIMARY KEY(id));
CREATE TABLE Payment(id INT NOT NULL AUTO_INCREMENT, accountID INT, amount DECIMAL(10,2), PRIMARY KEY(id));
(Don't laugh at me, it's not db schema creation lesson, just PHP tutorial, so we keep structure as simple as possible for brevity).
CakePHP suggest plural form of class name here, I do prefer singular form of database table name, the same as class name. It's more natural, then, to write select queries with JOINs.
Then we can create model classes in PHP that refers to above DB tables. Models are placed in CakePHP in files app/Model/ClassName.php:
Account.php:
<?php
class Account extends AppModel {
public $useTable = 'Account';
}
Payment.php:
<?php
class Payment extends AppModel {
public $useTable = 'Payment';
}
Note that you don't have to insert list of fields, they will be retrieved from DB using reflection.
In order to quickly check if DB connection works as expected on model level, let's try to load records from DB using existing controller file, app/Config/core.php. Let's add the following lines in this module:
public $uses = array('Account', 'Payment');
(...)
$this->Account->find('all');
$this->Payment->find('all');
$uses variable specifies the model classes that must be imported for this controller, $this->ModelClassName->* allows to call model queries/updates.
You can see resulting queries in page footer when you set debug mode to 2 (app/Config/core.php):
SELECT Account
.id
, Account
.name
, Account
.currency
FROM phpcake
.Account
AS Account
WHERE 1 = 1
SELECT Payment
.id
, Payment
.accountID
, Payment
.amount
FROM phpcake
.Payment
AS Payment
WHERE 1 = 1
Some criticism here:
- CakePHP doesn't raise an error (typical PHP sin) when there's a typo in $uses - one may miss simple typo there and wonder how her changes aren't visible - I don't know how to enable a warning/error in that situation (yet)
- Database table naming convention: CakePHP uses lower-case letter in plural form, I do prefer to use the same name as for model class, that can be fixed by "$useTable" mechanism
In next posts on this subject I'll investigate CakePHP pro's and con's and show how to overcome them. I'd like to review the following topics (among others):
- Lint-like functionality for PHP (pseudo static typing, exposing any mistakes as lint error)?
- Runtime error reporting to logs/e-mail (with full stacktraces)
- Built-in unit testing capability
- Integration testing using HTTP
The final goal is to find whether PHP-based system is able to scale (in terms of source code/project complexity). I do expect some basic features from programming environment to grow without pain and will verify if/how they can be achieved in PHP.