CakePHP: Database connection "Sqlite" is missing, or could not be created

Just downloaded 2.1.1 stable to read a SQLite database.

There’s a nice diagnostics page to welcome any developer, except it complained about my database config:

Database connection "Sqlite" is missing, or could not be created.

So I thought it might’ve been a missing driver, or something, so I looked in lib/Cake/Model/Datasource/Database:

-rw-rw-r-- 1 kzhiwei kzhiwei 20K 2012-03-25 18:30 Mysql.php
-rw-rw-r-- 1 kzhiwei kzhiwei 25K 2012-03-25 18:30 Postgres.php
-rw-rw-r-- 1 kzhiwei kzhiwei 16K 2012-03-25 18:30 Sqlite.php
-rw-rw-r-- 1 kzhiwei kzhiwei 23K 2012-03-25 18:30 Sqlserver.php

Everything looked fine to me, except that naggy error. It turns out additional stuff is required for PHP to talk to SQLite. If you’re on Oneiric Ocelot (11.10), you’re in luck. The package name is “php5-sqlite”, so you can just use a shell to:

sudo apt-get install php5-sqlite -y
sudo /etc/init.d/apache2 restart

Once that is done, use ‘:memory:’ for the database param, to make sure that the server setup is OK.

Lastly, adjust the filename of the database file; e.g. mine is ‘/app/webroot/trac.db’, so I simply use ‘trac.db’. Once this is done, you may encounter another error:

Database connection "SQLSTATE[HY000] [14] unable to open database file" is missing, or could not be created.

This is due to a permissions problem, so check if the www-data group owns webroot, trac.db. It should like:

drwxr-xr-x 6 kzhiwei www-data 4.0K 2012-04-25 10:46 .
-rw-rw---- 1 kzhiwei www-data 12M 2012-04-24 18:45 trac.db

Advertisements

CakePHP: File caching and debug=0

Recently I encountered a bug in the system I support. Basically it consumes a web service, writing the results into database-backed logs. The problem was that not every entry was being recorded correctly, resulting in inconsistent data between my system and the web service.

This coincided with a deployment of a new software release, and I puzzled over it, testing, re-testing and logging the sequence of calls. I couldn’t find the problem. In fact, I couldn’t even find appropriate entries in the log file. Great.

The problem that I was only testing on one web server — there were two — and it was deployment issue. Basically if you use the file storage engine for caching, CakePHP writes the schema to app/tmp/cache/models; e.g.

app/tmp/cache/models/cake_model_default_foo

The contents is a serialized version of the Foo model, and this will never be updated if debug is set to 0 in app/config/core.php. I’d written a script to delete such files, but I didn’t run the script on one of the two web servers, and that was the problem. I use saveAll(), and CakePHP compares keys against its cache, removing non-matching ones. One of those fields was set to not null, so saving failed all the time.

*updated* Take care when using CakePHP Model->id, Model->del(), Model->find()

Fixed a bug recently in our code base that had happened intermittently enough for me to ignore until recently, when the problem occurred twice in a week. There was this habit of using the ‘id’ attribute of the corresponding model; e.g.

$options = array(); // method params
$this->Foo->id = 'bar';
$this->Foo->baz($options);

Now this is a fairly handy syntax, but unexpected results are produced when used in conjunction with find() and del(); e.g.

$options = array('contain' => array());
$this->Foo->id = 'bar';
$this->Foo->find('first', $options)); // BUG: not equivalent to setting the conditions key of $options to array('Foo.id' => 'bar'). SQL is NOT "select * from foo where id='bar';"

The above find() returns the first (cached?) row is returned, which is incorrect of course.

Now for del():

$this->Foo->id = 'bar';
$this->Foo->del(); // BUG: not equivalent to $this->Foo->del('bar'). SQL is NOT "delete from foo where id='bar';"

Unfortunately, results will vary, depending on the database engine. I suspect that sometimes the first cached(?) row — without any WHERE clause — is returned, and it happens to be ‘bar’, hence delete works correctly. For your reference, we use Oracle 11g Release 2.

HTH,
Wayne

CakePHP, MongoDB primer

For your reference, and mine as well.

FIrst you need MongoDB to run on your system. It’s always easy on Debian/Ubuntu. Simply add:

deb http://downloads.mongodb.org/distros/ubuntu 9.4 10gen

to your /etc/apt/sources.lst for Ubuntu Jaunty (9.4).

$ sudo apt-get update; sudo apt-get install mongodb-stable

Once everything is downloaded — it takes awhile! — ps should show you something like:

$ ps -aef | grep mongodb
.
.
.
mongodb 8322 1 2 17:49 ? 00:02:59 /usr/bin/mongod --dbpath /var/lib/mongodb --logpath /var/log/mongodb/mongodb.log run --config /etc/mongodb.conf

Next you need PECL mongo, which allows PHP to communicate with MongoDB:

$ pecl install mongo

Remember to add ‘extension=mongo.so’ to your php.ini file! For my implementation, I created a separate mongo.ini in /etc/php5/conf.d/.

And then you’ll need the MongoDB datasource:

cd path/to/cakephp/app/plugins/; git clone git://github.com/ichikaway/cakephp-mongodb.git mongodb

Alternatively if you don’t have git, you can always download the source from https://github.com/ichikaway/cakephp-mongodb. Once the clone (or download) is complete, you should have mongodb/ in app/plugins.

app/config/database.php should have a config like:

var $mongo = array(
'database' => 'someApp',
'driver' => 'mongodb.mongodbSource',
'host' => 'localhost',
'port' => 27017
);

Your model should have, at the very least:

var $mongoSchema = array('type' => 'string'),
'created' => array('type' => 'date'),
'modified' => array('type' => 'date')
);
var $useDbConfig = 'mongo';

For such testing, CakePHP scaffolding will come in handy. Once some data is inserted, connect to the console:

$ mongo localhost:27017/someApp
MongoDB shell version: 1.6.4
connecting to: localhost:27017/someApp
> db.somes.count();
44220
>

302 error in CakePHP with SWFUpload

I encountered this issue in one of my recent projects.

Basically I use SWFUpload to post files to a CakePHP controller. It was working beautifully, until I inadvertently removed 1 (yes, just one) line of code in beforeFilter():

$this->Auth->allow("swfupload");

I was using the Auth component, and without the line above to allow “not logged in” access to the controller action, then we have a problem.

Hope this helps somebody.

Embedding Flash in HTML, versus iframe

I recently received a request to develop a CakePHP website. The interface consists of a container 800 by 250 pixels width, with practically no chrome (company name, copyright info) whatsoever. It’s shows calendar entries for a given period, and users can click to view entry details (in a Thickbox). I thought the design was rather simplistic, and gave it no further thought. The project was completed in about 2 days, and I spent an additional 1.5 days making some minor changes.

Today I realized that simple design was because it was intended to replace a Flash-based calendar on the client’s homepage. I did a test with iframe, but it doesn’t work well, because the Thickbox (with entry details) is contained within the iframe.

Note to self: Clarify intended deployment method before giving price information. No hard feelings of course, since they might not understand that embedding Flash is not the same as iframe. Now it’s likely I’ll need to spend more time doing it right; i.e. template-ize their HTML, and then put in the (micro) website that I completed earlier.