Hi all, I've been working on a catch-all CakePHP Plugin for my company with a number of coligues and I think we've developed enough to blog about some of the cooler features it offers. I'm going to go through them one at a time and post in depth on each one.
The first one I want to talk about is Versioning.
Model Versioning -- The Ability to UNDO
Have you ever wanted to go back in time with content of a record? Do you have multiple users who can make changes on your data and you'd like a way to track who did what, when and a way to revert to a previous version? How about being able to snapshot your record and include other model associations in your version of that snapshot? VersionableBehavior to the rescue!
Attach to any model to creating versions of current state on save for later restoration.
Uses the AuthComponent to log the user doing the save by default.
The plugin requires a single table icing_versions to be installed in your application. This is the core table that will handle any model's current version data and allow for you to not only go back and time and see what was changed, but who changed it, when, and what other models were affected by the change. You can also restore from previous version by either knowing the version id, or by simply saying "go back 2 revisions" and the plugin handles the rest.
Install Versionable
Run the schema into your database to create icing_versions table.
cake schema create -p Icing
You should see
icing_versions in your database
Usage Examples
Bind to model you want to auto-version on save
Default Settings:
'contain' => array(), //only version the current model
'versions' => false, //unlimited versions
'minor_timeframe' => false, //do not mark for minor versions
'bind' => false, //don't bind versions on find
)
public $actsAs = array('Icing.Versionable'); //default settings
public $actsAs = array('Icing.Versionable' => array(
'contain' => array('Hour'), //contains for relative model to be included in the version.
'versions' => '5', //how many version to save at any given time (false by default unlimited)
'minor_timeframe' => '10', //Mark all previous versions if saved within 10 seconds of current version. Easily cleanup minor_versions
'bind' => true, //attach IcingVersion as HasMany relationship for you on find and if contained
));
Settings Explained
contain : Containable array of other associated models to also version along with each version. If your model's snapshot also depends on other tables and models, simply add them to the
contain option so it's included in the versioning (and restorable)
versions : Number of versions to allow on any paticular record (default unlimited). This is essentially the "undo" limit on your model your versioning. If it's a rather active record and you have unlimited versions the versions table will eventually get very large and slow, consider using an versions limit that will automatically clean up after itself.
minor_timeframe : Seconds between saves to mark previous as a minor version. Minor versions are then cleaned off periodically on demand by the user. The idea is if you make 10 saves within a minute, 9 of those saves are minor versions and really shouldn't be kept as major changes, whereas the last save includes all the changes and is the major version. Default false (minor versions are not kept track of)
bind : Default False. If true on non-contained retrevals of data on the versioned model, all related versions for each record will also be returned to the user. This is a dynamic bind. You can turn this feature off completely and just use it by containing on the model.
$this->find('first', array(
'Model.id' => $id,
),
'IcingVersion' => array('order' => 'IcingVersion.created DESC')
)
));
The dynamic bind will happen for you automatically, you don't have to define your custom bind on the Model.
Restoring from a version
Restore version id.
$this->Model->restoreVersion('50537471-ba08-44ae-a606-24e5e017215a'); //restores version id 50537471-ba08-44ae-a606-24e5e017215a
Restore from a version id and don't create a new version before restoring by passing boolean false in restoreVersion
$this->Model->restoreVersion('50537471-ba08-44ae-a606-24e5e017215a', false); //restores version id 50537471-ba08-44ae-a606-24e5e017215a and won't create a new version before restoring.
Restore by just iterating back in time on a record. No need to remember the version ID.
Restore second version back from most recent on Model with the record id of 3
$this->Model->restoreVersion(2, 3); //restores the second version back from most recent on Model id 3
Restore the second version back from the most recent Mode with record id of 3 and don't create a new version before restoring.
$this->Model->restoreVersion(2, 3, false); //restores the second version back from most recent on Model id 3 and doesn't create a new version before saving
Seeing the difference between versions
You can easily see the difference between two different versions (or the current version HEAD) of the current record at any time.
Get the difference between a specific version and the current state of the record the version is of.
$result = $this->Model->diffVersion('50537471-ba08-44ae-a606-24e5e017215a'); //Gets the diff between version id and the curent state of the record.
Get the difference between two specific versions.
$result = $this->Model->diffVersion('50537471-ba08-44ae-a606-24e5e017215a', '501234121-ba08-44ae-a606-2asdf767a'); //Gets the diff between two different versions.
Save without creating a version
You can make a save on your versionable model without creating a new version by passing 'create_version' => false as an option to the Model save or saveAll options.
$this->Model->save($data, array('create_version' => false));
Enjoy
As always, enjoy and comment!
I ran into a very peculiar problem when trying to run my CakePHP tests on my mac the other day. I would go to run my cake test command and it would just exit immediately -- no errors, no output, nothing.
[nwb@cap:~/Websites/HH/app → dev]$ cake test
[nwb@cap:~/Websites/HH/app → dev]$
Odd, the rest of my shell commands worked perfectly. I know I had PHPUnit installed via mac ports so what was the deal?
So I started digging into the core with
die() commands to narrow down the problem, turnes out it was in the CakeTestSuiteDispatcher class when trying to run the loadTestFramework() function.
//lib/Cake/TestSuite/CakeTestSuiteDispatcher.php
public function loadTestFramework() {
$found = $path = null;
if (@include 'PHPUnit' . DS . 'Autoload.php') {
$found = true;
}
if (!$found) {
foreach (App::path('vendors') as $vendor) {
if (is_dir($vendor . 'PHPUnit')) {
$path = $vendor;
}
}
if ($path && ini_set('include_path', $path . PATH_SEPARATOR
. ini_get('include_path'))) {
$found = include 'PHPUnit' . DS . 'Autoload.php';
}
}
return $found;
}
The issue is on line 141:
if (@include 'PHPUnit' . DS . 'Autoload.php') {
$found = true;
}
CakePHP tries to include PHPUnit/Autoload.php which is there, but Autoload.php requires_once() on PHP/CodeCoverage/Filter.php (which wasn't installed). For some stupid reason CakePHP decided to suppresses this error message, which is just wrong. I understand they want to also allow you to install PHPUnit in your vendors path, but then update the paths before the include instead of trying it out first and suppressing the error message. I'd prefer the extra bloat to loading tests than to have it break without any error message. Suppression doesn't save a fatal_error it just leaves the user guessing as to why nothing happened.
Once I understood the problem the fix was simple, install php5-code-coverage and add the include_path to php.ini
$ sudo port install php5-code-coverage
;php.ini file
include_path = ".:/opt/local/lib/php/"
Stop and start PHP (apache, or fpm) and tried again. I'm back to testing in CakePHP.
Hope that helps someone else,
Nick
Easy interaction, file uploading/downloading/management of Rackspace's CDN service.
CakePHP, CloudFiles, and You -- The Perfect Threesome
I've had the fun task of creating an interface to the popular Rackspace Cloud Files service. If you don't know what that is, its a service that allows you to store unlimited amounts of data remotely and serve them on the fly. You only pay for what storage you use and how much bandwidth you use. It's extremely affordable and a great way to speed up your application by off-hosting your assets (browsers can only download 2 concurrent assets per domain). To read more about the service visit here:
http://www.rackspace.com/cloud/cloud_hosting_products/files/
This plugin utilizes the php-cloudfiles class created by Rackspace.
Requirements
This plugin required CakePHP 2.x, PHP 5.x, and because it utilizes php-opencloud, any requirements defined by that package
https://github.com/rackspace/php-opencloud
Install
Now that your sold on the service, You can install this plugin to help jump start your CDN management system. There are two ways to install the plugin:
Git Installation
git clone git://github.com/webtechnick/CakePHP-CloudFiles-Plugin.git app/Plugin/CloudFiles
cd app/Plugin/CloudFiles
git submodule init
git submodule update
Manual Installation
Download
https://github.com/webtechnick/CakePHP-CloudFiles-Plugin plugin into
app/Plugin/CloudFiles
Download
https://github.com/rackspace/php-opencloud into
app/Plugin/CloudFiles/Vendor/php-opencloud
Configuration
Ensure the plugin is loaded in
app/Config/bootstrap.php by calling
CakePlugin::load('CloudFiles');
//app/Config/bootstrap.php
CakePlugin::load('CloudFiles');
Create a file
app/Config/cloud_files.php with the following:
//app/Config/cloud_files.php
'server' => 'US', //UK
'username' => 'your_username', //your username
'api_key' => 'API_KEY', //your api key
)
);
Example of this configuration file is in
app/Plugin/CloudFiles/Config/cloud_files.php.default
Usage Examples
The majority of the plugin is in a static library called CloudFiles, some of you purists would say "Hey Nick, you're interacting with a remote service, this should be in a datasource!" And normally I would say you're correct, although a little rude. ;-) However, a datasource is normally coupled with a model and I wanted a simple class to be called statically and preform the REST functions I required. If I created a datasource I couldn't make it static, and it wasn't as portable as I wanted the class to be. Plus, in CakePHP 2.x a lot of the core classes are moving to Libraries instead for easy portability across your application -- I'm following the suit.
You must first App::uses the library in whatever file you want near the top.
App::uses('CloudFiles','CloudFiles.Lib');
Upload a file to Rackspace
Uploads a local file to the specified container in Rackspace
$cdn_url = CloudFiles::upload('/path/to/image.jpg','container_name');
Download a file from Rackspace
Download a remote file on Rackspace in a specific container to a local file
CloudFiles::download('image.jpg', 'container_name', '/local/path/to/image.jpg');
Delete a file from Rackspace
Delete a file from a specific container on Rackspace
CloudFiles::delete('image.jpg','container_name');
List files on Rackspace
List files in a specified container on Rackspace
App::uses('CloudFiles','CloudFiles.Lib');
//Get all files in container
$files = CloudFiles::ls('container_name');
//Get files in subfolder
$files = CloudFiles
::ls('container_name', array(
'path' => 'pictures/animals'
));
//Get files starting with a prefix
$files = CloudFiles
::ls('container_name', array(
'prefix' => 'cake'
));
//Limit the files returned
$files = CloudFiles
::ls('container_name', array(
'limit' => 10
));
//Limit the files returned, starting at marker
$files = CloudFiles
::ls('container_name', array(
'limit' => 10,
'marker' => 30
));
Public or Streaming URL of a file on Rackspace
Get the URL of an object in Rackspace (streaming or public)
$url = CloudFiles::url('image.jpg','container_name');
$stream = CloudFiles::stream('movie.mov', 'container_name');
There is also a helper class to assist image and streaming retrieval
//Some Controller
public $helpers = array('CloudFiles.CloudFiles');
//Some View
echo $this->CloudFiles->image('image.jpg','container_name');
echo $this->CloudFiles->stream('movie.mov', 'container_name');
echo $this->CloudFiles->url('some_file.txt', 'container_name');
List containers on Rackspace
List all containers on Rackspace
//Get all containers
$containers = CloudFiles::listContainers();
//Limit the containers returned
$containers = CloudFiles
::listContainers(array(
'limit' => 2
));
//Show only public containers
$containers = CloudFiles
::listContainers(array(
'only_public' => true
));
Create container on Rackspace
Created a container on Rackspace, defaults to public container (CDN)
$Container = CloudFiles::createContainer('css');
//Create a non-public container
$Container = CloudFiles::createContainer('no_public', false);
Delete a container on Rackspace
Delete a container on Rackspace, notice container must be empty.
CloudFiles::deleteContainer('container_name');
Enjoy!
As always, comments are appreciated. I hope you enjoy the plugin.
Setup CakePHP 2.0 nginx + php-cgi + mysql + phpmyadmin on OS X Lion
My company just bought me a new macbook and while it shouldn't have taken me as long as it did to setup my development environment for CakePHP, there were a few gotchas that I thought maybe you could learn from my mistake or perhaps correct my wrongs. So without futher adue, lets jump right in.
Mac Ports is our friend!
Coming to mac from Ubuntu, I'm really (read extremely) thankful macports exists. Apple's App Store is nice, but as of now I couldn't find any decent development tools in the app store to easily download and setup. I'm comfortable with the command line so as soon as I discovered macports I was right at home.
To install macports you'll first need to install xcode from the Apple App Store. Once you've installed xcode, simply hop over to
http://www.macports.org/install.php to download the latest dmg for lion.
Once you've successfully installed macports you're ready to rock and roll. I then install nginx, php-cgi, php5, php5-gd, php5-mysql, php5-mcrypt using the command line.
Mac comes with PHP installed already, but instead of trying to get that version to work with everything I instead just used macports to install a clean version of PHP for me to configure to how I like things to run completely separate from the mac's default setup.
Nginx Install
Enough foreplay, lets get to it, install nginx first.
Copy working configuration default into its own configuration
sudo cp /opt/local/etc/nginx/nginx.conf.default /opt/local/etc/nginx/nginx.conf
If you want to launch nginx on system startup simply run the plist insalled by the port:
sudo launchctl load -w /Library/LaunchDaemons/org.mackports.nginx.plist
TIP: start and stop nginx on demand from the command line, but don't do it yet! We have to configure it more first.
Start Nginx on demand:
Stop Nginx on demand:
Installing MySQL
EDIT: I've gone back and re-installed mysql via macports for easy package management. Skip to the next section to install that way, or continue reading on how to install MySQL from the official package from dev.mysql.com
I could have gone with macports, and in retrospect, perhaps I should have, but I actually went ahead and installed the official package from dev.mysql.com here:
http://dev.mysql.com/downloads/mysql/
I selected the 64bit arch Mac OS ver 10.6, which works fine under Lion (10.7). This version comes with a nice little system preferences pane so you can stop and start it directly from your mac system preferences -- slick. Once you've installed that lets move onto the rest.
The default install of mysql doesn't include a password for your root login, this won't play nice with phpMyAdmin when we finally install it, so go ahead and add a root password by running this command:
sudo mysqladmin -u root password NEWPASSWORD
If you get a mysqladmin command not found, make sure to add /usr/local/mysql/bin path in your ~/.profile and try again.
# .profile
export PATH=/opt/local/bin:/opt/local/sbin:/usr/local/mysql/bin:$PATH
To reload your profile type:
Installing MySQL via MacPorts
I had a change of heart and decided to install mysql5 via MacPorts to keep it all under one package manager. If you've already installed MySQL using the package provided by dev.mysql.com then just skip ahead to Installing PHP with PHP-CGI. Otherwise keep reading.
Install MySQL via macports
sudo port install mysql5-server
Once complete you'll need to start the mysql daemon and set a root password
#start mysql daemon
sudo mysqld_safe5 &
sudo mysqladmin5 -u root password NEWPASSWORD
You will need to start mysql daemon whenever you wish to use it. I'd sugget making a shell command or an alias in your
~/.profile file.
#~/.profile
alias mysql='/opt/local/bin/mysql5'
alias start_mysql='sudo mysqld_safe5 &'
alias stop_mysql='mysqladmin5 --user=root --password=NEWPASSWORD shutdown'
Now you're all set with MySQL from MacPorts.
Installing PHP with PHP-CGI (FastCGI)
Like I said before, Lion comes with its own PHP install, but I much prefer to work with the package manager whenever possible, so I'm installing a fresh copy of PHP in /opt/local. Let's also install php5 with fastcgi (we need for nginx to talk to PHP) along with some of the nicer libraries we want access to for phpMyAdmin and mysql.
sudo port install php5 +fastcgi fcgi php5-gd php5-mysql php5-mcrypt
Starting php-cgi:
php-cgi -q -b 127.0.0.1:9000 &
Stopping PHP-CGI:
You'll need to start the php-cgi whenever you want nginx to talk to PHP via CGI daemon. I ended up writting a little bash to start this for me with a simple command and saved it to ~/bin/start_php_cgi.sh
#!/bin/bash
php-cgi -q -b 127.0.0.1:9000 &
Configure Nginx
Open up /opt/local/etc/nginx/nginx.conf in your favorite editor and configure it:
[geshi=code]
#user nobody;
worker_processes 1;
error_log /opt/local/logs/error.log;
pid /opt/local/logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
access_log /opt/local/logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
gzip on;
server {
listen 80;
server_name localhost;
root /var/www;
index index.html index.htm index.php;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
#root /var/www;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
This is a basic nginx setup to run with PHP-CGI nicely. You'll have to add a little more to get it to work nicely with a CakePHP install, but we'll get to that soon enough. As you can see, I like to root things in /var/www, make sure this directory exists for you
Lets write a testfile to see what we've got so far!
sudo echo "<?php echo phpinfo(); ?>" > /var/www/index.php
Start nginx, php-cgi and then navigate to localhost/index.php to see your hard work in all its glory.
Installing phpMyAdmin
Grab the latest version of phpMyAdmin from:
http://www.phpmyadmin.net/home_page/downloads.php (version 3.4.7 when writing) and unpack it wherever you'd like (I put mine in /opt/local/phpMyAdmin).
Since I like to keep my route in /var/www as my default for nginx, I then created a symlink between where I installed phpMyAdmin and /var/www/phpMyAdmin
sudo ln -s /opt/local/phpMyAdmin /var/www/phpMyAdmin
Now navigate to http://localhost/phpMyAdmin and you should be greeted with a nice phpMyAdmin screen.
Install CakePHP 2.0
If you've gotten this far, GOOD JOB! To get to this point it took me nearly an entire day. We're on the home stretch, just a few more things to get CakePHP 2.0 to bake and work on your new sparkly mac!
Grab the latest version of CakePHP from github, you may first have to install git. I grabbed a copy from
http://git-scm.com but you could install it using macports if you want. After you have git up and running run:
[geshi=code]
git clone
https://github.com/cakephp/cakephp.git
Once you've cloned the repository note the location where you cloned it and update your .profile with an alias so you can run cake commands from anywhere
# .profile
alias cake="/path/to/cakephp/lib/Cake/Console/cake"
Reload your profile
Now lets bake our first app!
cd path/to/where/you/want/app
cake bake app
Now you'll need to configure a new nginx virtual host for your cakePHP app. I personally like to create new site files and name them for each virtual site I want to run.
For this example I would save a site in /opt/local/etc/nginx/testapp.conf with the following:
[geshi=code]
server {
listen 80;
server_name dev.testapp.devlocal;
access_log /var/log/nginx/dev.testapp.devlocal.access.log;
error_log /var/log/nginx/dev.testapp.devlocal.error.log;
rewrite_log on;
root /path/to/cakephp/app/testapp/app/webroot;
index index.php;
# Not found this on disk?
# Feed to CakePHP for further processing!
if (!-e $request_filename) {
rewrite ^/(.+)$ /index.php?url=$1 last;
break;
}
# Pass the PHP scripts to FastCGI server
# listening on 127.0.0.1:9000
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_intercept_errors on; # to support 404s for PHP files no$
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to .htaccess files,
# git & svn repositories, etc
location ~ /(\.ht|\.git|\.svn) {
deny all;
}
}
Then I would add a single line to the nginx.conf file right before the http enclosure
[geshi=code]
include testapp.conf;
Stop and start nginx:
sudo nginx -s stop
sudo nginx
Add your new server_name to your /private/etc/hosts file
[geshi=code]
127.0.0.1 localhost dev.testapp.devlocal
Now navigate to
http://dev.testapp.devlocal on your machine and you should see your brand new baked CakePHP 2.0 app! Congratulations, happy baking!
I hope this tutorial helped, comments are appreciated.
UPDATE 3
This update is now complete, all changes have been merged into master and you should update your plugins accordingly. The newest version is v3.1.1. Thank you all for your patience and continued support of the plugin.
UPDATE 2
I've forced the master branch of the plugin to be version v2.5.0 (the stable version). You can simply checkout the plugin normally to see the currently stable and functional version v2.5.0. All the 3.0 changes have been merged into a new branch called php_sdk_3. I will be updating and testing this branch and will not release it back to master until all features are working 100%. I will keep you updated as I make progress.
For now, simply checkout the master version of the plugin for the latest stable release.
$ cd path/to/plugin/repository
$ git checkout master
Thanks,
Nick
UPDATE
It appears I've jumped the gun a bit on the upgrade to v3.0. Looks like the good folks at Facebook still haven't linked up the fbxml and the PHP SDK entirely, leaving some features to partially work and others to be flat out broken. Until these issue are resolved please use the v2.5.0 version of the plugin.
For the time being please use the tagged version v2.5.0 of the plugin.
$ cd path/to/plugin/repository
$ git checkout v2.5.0
Or you can simply download it directly from here:
https://github.com/webtechnick/CakePHP-Facebook-Plugin/zipball/v2.5.0
I will be keeping a close eye on this and be update this blog post as well as the plugin as soon as I iron out all the issues the upgrade to 3.0 has caused.
Thanks for your understanding.
Original Post...
Two blog posts in one week! You're in for a special treat! Facebook finally updated their PHP SDK v3 to talk to their Javascript SDK which was holding the plugin back from updating along with the Facebook OAuth system. Now that Facebook has updated their PHP SDK, The plugin has been updated accordingly.
By updated your plugin you won't be affected by the October 1st OAuth only deadline. Please update your plugin to be sure your app wont be locked out on October 1st 2011.
In addition to utilizing OAuth, there are two new features in version 3.0.
1) Send Button -- easily send the current page to a friend via email
2) Facebook Pre Populated Registration forms.
So without further adieu, lets get into it.
Send Button
Send buttons are new social media plugins to easily share a page via email (no facebook credentials required).
//Show a send button default to current page.
$this->Facebook->sendButton();
//Show a send button to a custom page
$this->Facebook->sendButton('http://www.webtechnick.com');
//Show a send button with custom options
$this->Facebook->sendButton('http://www.webtechnick.com', array(
'colorscheme' => 'dark'
));
Facebook Registration
New social media registration plugin allows a user to fill out a registration form auto-populated by their information in their Facebook profile. The setup is simple. Use the FacebookHelper::registration() to show the registration form in an iframe.
//Show registration form with default fields
$this->Facebook->registration();
//Show registration form with custom fields and width
$this->Facebook->registration(array(
'fields' => 'name,email,location,gender,favorite_team',
'width' => '500'
));
By default, once the user clicks "Register" the post is sent back to the same page the registration form was shown on. In your controller action simply check for registrationData and do with the data what you will (create/update a user etc...)
function registration(){
if($user = $this->Connect->registrationData()){
//We have a registered user, look at it and do something with it.
}
}
You can change where you want the user to post to by setting 'redirect-uri' in the registration helper method.
//Show registration form with custom url
$this->Facebook->registration(array(
'redirect-uri' => 'http://www.example.com/facebook_registration',
));
Hope you enjoy! As always, donations are appreciated.
It's been a while since I've posted, I've been doing a lot of work on various fun projects and just haven't had the time to formally post about all the cool things I've been doing. I figured with a new plugin deserves a new blog post. So, here I am -- lets talk log files and how they suck.
Log files.. more like lame files
Log files can be handy, they can serve as a barometer for how well your app is operating, and give you some clue as to how your users are using your app (or more importantly -- breaking your app). The default CakeLog settings writes log entries to a file in your
/tmp/logs/ directory under the type of log you're specifying.
For example:
CakeLog::write('error', 'This will be a new entry in the /tmp/logs/error.log file');
Will write to
/tmp/logs/error.log
This is all well and good if you're running your application on a single server, and you're a masochist who likes to copy over large log files, and open them up in an editor to actually search through it.
However, even though I'm a web developer, I'm not a masochist -- despite popular opinion. So I've developed a super simple plugin to take all those handy dandy log messages scattered around an application and save them to a database logs table instead. The benefit of this strategy is three fold.
1) Scale-ability -- as you add more app servers to handle traffic you're logs will still all be consolidated in one place.
2) Easier searches, with the built in admin interface searching through logs can be done directly in the app.
3) Free extra information with each log entry. With each log entry the requesting url, hostname, ip, and referrer is automatically logged along side it.
Setup
Setup is simple. Like any other plugin you'll need to clone the repository into your
app/plugins/database_logger directory, and run the schema into the database to create the required logs table.
# clone plugin
git clone git://github.com/webtechnick/CakePHP-DatabaseLogger-Plugin.git app/plugins/database_logger
# run schema into database to create logs table.
cake schema create database_logger -plugin database_logger
Next, you'll need to add two lines to your
app/config/bootstrap.php file to configure your CakeLog to log to the new database logger instead of the default FileLog.
//app/config/bootstrap.php
App::import('Core','CakeLog');
CakeLog
::config('default', array('engine' => 'DatabaseLogger.DatabaseLogger'));
Usage
Anywhere in your app where you call $this->log() or CakeLog::write the database logger will be used.
$this->log('This is a detailed message logged to the database','error');
CakeLog::write('error', 'This is a detailed message logged to the database');
Navigate to
http://www.example.com/admin/database_logger/logs to view/search/delete your logs.
Enjoy!