<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PRONIQUE Software</title>
	<atom:link href="http://www.pronique.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.pronique.com</link>
	<description>PHP Software Development Services, Open Source Software, CakePHP Applications</description>
	<lastBuildDate>Mon, 27 Feb 2012 14:48:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>How to Create your own Scaffolding plugin for CakePHP 2</title>
		<link>http://www.pronique.com/blog/how-to-create-your-own-scaffolding-plugin-for-cakephp2?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-create-your-own-scaffolding-plugin-for-cakephp2</link>
		<comments>http://www.pronique.com/blog/how-to-create-your-own-scaffolding-plugin-for-cakephp2#comments</comments>
		<pubDate>Fri, 24 Feb 2012 18:05:46 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[CakePHP 2.0]]></category>
		<category><![CDATA[CakePHP 2.1]]></category>
		<category><![CDATA[CakePHP 2.x]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[Scaffolding]]></category>
		<category><![CDATA[TreeBehavior]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1873</guid>
		<description><![CDATA[CakePHP&#8217;s builtin scaffolding is great but sometimes I wish it would do more or behave a little differently. Until recently,<a href="http://www.pronique.com/blog/how-to-create-your-own-scaffolding-plugin-for-cakephp2" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p><img itemprop="image" src="http://www.pronique.com/wp-content/uploads/2012/02/cakephp-scaffolding.png" alt="" title="CakePHP Scaffolding" width="125" height="125" class="alignright size-full wp-image-1890" />CakePHP&#8217;s builtin scaffolding is great but sometimes I wish it would do more or behave a little differently.  Until recently, I assumed this couldn&#8217;t be accomplished with the way scaffolding was baked in. Thanks to the clever way CakePHP handles includes, we can easily use our own scaffolding implementation by adding 2 lines to AppController.</p>
<p>In this tutorial we will built a scaffolding plugin that is aware of the TreeBehavior<span id="more-1873"></span>.  If you use scaffolding for a Tree model the parent dropdown in the add/edit view is unordered and confusing.</p>
<div id="attachment_1923" class="wp-caption alignnone" style="width: 389px"><img itemprop="image" src="http://www.pronique.com/wp-content/uploads/2012/02/Parent-Category-With-TreeAwareScaffold.png" alt="" title="Non-indented Parent Association dropdown box" width="379" height="271" class="size-full wp-image-1923" /><p class="wp-caption-text">Non-indented Parent Association dropdown box</p></div>
<p>In our scaffolding plugin we will generate ordered/indented dropdowns for all belongsTo associations that implement the TreeBehavior.  This is accomplished by calling <span class="path">Model->generateTreeList()</span> instead of <span class="path">Model->find(&#8216;list&#8217;)</span>.  Here is an example.</p>
<div id="attachment_1924" class="wp-caption alignnone" style="width: 420px"><img itemprop="image" src="http://www.pronique.com/wp-content/uploads/2012/02/Indented-Dropdown-Parent-Category-TreeBehavior.png" alt="" title="Dropdown created with generateTreeList() TreeBehavior" width="410" height="235" class="size-full wp-image-1924" /><p class="wp-caption-text">Indented Parent dropdown box</p></div>
<p>To get started you want to first setup our own scaffolding implementation housed inside a plugin.</p>
<ol class="steps">
<li>
<div>
<h3>Create a new CakePHP plugin</h3>
<pre class="cli">
sudo cake bake plugin TreeAwareScaffold
</pre>
<p class="note">Note: The name <strong>TreeAwareScaffold</strong> is just for illustration purposes.  You can name your plugin whatever you want, but keep it descriptive. Oh, and don&#8217;t forget to share it with others via <a rel="external nofollow" href="http://www.github.com">github</a>.</p>
</div>
</li>
<li class="step-1">
<div>
<h3>Create the Scaffold class</h3>
<p>Copy the core Scaffold class into your plugin as a starting point.</p>
<pre class="cli">
cp lib/Cake/Controller/Scaffold.php app/Plugin/TreeAwareScaffold/Controller/
</pre>
<p>Modify the Scaffold class to use the ScaffoldView class from your plugin (we will create it in the next step).  Change line 147 to&#8230;</p>
<pre class="brush: php">
$this->controller->viewClass = 'TreeAwareScaffold.Scaffold';
</pre>
</div>
</li>
<li class="step-2">
<div>
<h3>Extend the Scaffold class to use generateTreeList() for Tree enabled models</h3>
<p>We want scaffolding to generate a nice indented dropdown list for our add and edit scaffold views but only for belongsTo associations that have the Tree behavior<br />
enabled.</p>
<p>To accomplish this, add the following method to <span class="path">app/Plugin/TreeAwareScaffold/Controller/Scaffold.php</span></p>
<pre class="brush: php">
/**
* Detect if Tree Behavior is enabled on $Model
*
* @param mixed $Model
*/
protected function _isTree( $Model ) {
    if ( $Model->hasMethod('generateTreeList') ) { return true; }
    return false;
}
</pre>
<p>Next, Change line 274 of <span class="path">app/Plugin/TreeAwareScaffold/Controller/Scaffold.php</span> from&#8230;</p>
<pre class="brush: php">
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
</pre>
<p>to the following&#8230;</p>
<pre class="brush: php">
if ( $this->_isTree( $this->ScaffoldModel->{$assocName} ) ) {
    $this->controller->set($varName, $this->ScaffoldModel->{$assocName}->generateTreeList(null, null, null, '---') );
} else {
    $this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
}
</pre>
</div>
</li>
<li class="step-3">
<div>
<h3>Hide lft,rght fields in scaffolding</h3>
<p>The lft,rght columns are characteristic of Models with the Tree behavior enabled.  We don&#8217;t really want to see or edit them so the following will hide them in our scaffold views.</p>
<p class="note">Note: Manually editing lft,rght field values is the primary reason for inconsistent tree tables so hiding them is a great idea!</p>
<p>Replace line 137 of <span class="path">app/Plugin/TreeAwareScaffold/Controller/Scaffold.php</span>&#8230;</p>
<pre class="brush: php">
$scaffoldFields = array_keys($this->ScaffoldModel->schema());
</pre>
<p>with the following&#8230;</p>
<pre class="brush: php">
/**
* Hide the lft,rght fields for Tree Models
* this will not work if left and right fields
* are named anything other than lft,left,rght,right
*/
if ( $this->_isTree( $this->ScaffoldModel ) ) {
    $fields = $this->ScaffoldModel->schema();
    unset($fields['lft']); unset($fields['left']); unset($fields['rght']); unset($fields['right']);
    $scaffoldFields = array_keys( $fields );
} else {
    $scaffoldFields = array_keys($this->ScaffoldModel->schema());
}
</pre>
</div>
</li>
<li class="step-4">
<div>
<h3>Create the ScaffoldView class and scaffolding view templates</h3>
<p>Copy the core Scaffold View class into your plugin as a starting point.</p>
<pre class="cli">
cp lib/Cake/View/ScaffoldView.php app/Plugin/TreeAwareScaffold/View/
</pre>
<p>Copy the core scaffolding views(form.ctp, index.ctp, view.ctp) into your plugin.</p>
<pre class="cli">
cp -r lib/Cake/View/Scaffolds app/Plugin/TreeAwareScaffold/View/
</pre>
</div>
</li>
<li class="step-5">
<div>
<h3>Tell ScaffoldView to load scaffolding views from the plugin</h3>
<p>Edit <span class="path">Plugin/TreeAwareScaffold/View/ScaffoldView.php</span>, changing line 67-68 from&#8230;</p>
<pre class="brush: php">
$names[] = $this->viewPath . DS . $subDir . $scaffoldAction;
$names[] = 'Scaffolds' . DS . $subDir . $name;
</pre>
<p>to the following&#8230;</p>
<pre class="brush: php">
$names[] = $this->viewPath . DS . $subDir . $scaffoldAction;
$names[] = '..' . DS . 'Plugin' . DS . 'TreeAwareScaffold' . DS . 'View' . DS .'Scaffolds' . DS . $subDir . $name;
$names[] = 'Scaffolds' . DS . $subDir . $name;
</pre>
</div>
</li>
</ol>
<p>That&#8217;s it! You are ready to test drive your new scaffolding plugin.</p>
<h2>Part 2: Using TreeAwareScaffold in your app</h2>
<ol class="steps" start="1">
<li class="step-1">
<div>
<h3>Enable the TreeAwareScaffold plugin</h3>
<p>Add the following line to <span class="path">app/Config/bootstrap.php</a></p>
<pre class="brush: php">
CakePlugin::load('TreeAwareScaffold');
</pre>
</div>
</li>
<li class="step-2">
<div>
<h3>Configure AppController to use the new Scaffolding plugin</h3>
<pre class="brush: php">
App::uses('Scaffold', 'TreeAwareScaffold.Controller');
App::uses('ScaffoldView', 'TreeAwareScaffold.View');
</pre>
</div>
</li>
<li class="step-3">
<div>
<h3>Create a Category Model and enable the Tree behavior</h3>
<p><span class="path">app/Model/Category.php</span></p>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');
class Category extends AppModel {

    public $displayField = 'name';

    public $actsAs = array('Tree');

    public $hasMany = array(
        'ChildCategory'=>array(
        'className'=>'Category',
        'foreignKey'=>'parent_id',
        'dependent'=>true
        )
    );

    public $belongsTo = array(
        'ParentCategory'=>array(
        'className'=>'Category',
        'foreignKey'=>'parent_id'
        )
    );

}
</pre>
</div>
</li>
<li class="step-4">
<div>
<h3>Create the associated table in MySQL</h3>
<pre class="brush: sql">
CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `parent_id` int(10) unsigned DEFAULT NULL,
  `lft` int(10) unsigned DEFAULT NULL,
  `rght` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
</pre>
</div>
</li>
<li class="step-5">
<div>
<h3>Create a CategoriesController with Scaffolding</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppController', 'Controller');

class CategoriesController extends AppController {

    public $scaffold;

    public $paginate = array(
        'order'=>array(
            'Category.lft'=>'asc'
        ),
        'limit'=>100
    );

}
</pre>
</div>
</li>
</ol>
<h2>More Scaffold Plugins</h2>
<p>A list of scaffolding plugins that you can use in your CakePHP Application.  If you have created a scaffolding plugin, let us hear about in the comments below and I will add it to this list.</p>
<p><a href="https://www.github.com/pronique/CakePHP-TreeAwareScaffold">TreeAwareScaffold</a> &#8211; If you want to try out the plugin we created in this tutorial without following along step-by-step you can download the plugin from <a hreh="https://www.github.com/pronique/CakePHP-TreeAwareScaffold">github</a>!</p>
<h4>Keywords:</h4><ul><li>cakephp generate list scaffolding</li><li>cakephp 2 1 tutorial</li><li>cakephp</li><li>cakephp 2 tutorials</li><li>cakephp2 tree</li><li>cakephp tutorial</li><li>cakeDC export cvs</li><li>how to export into csv in cakephp2 0</li><li>create plugin in cakephp</li><li>cakephp scaffold export</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/how-to-create-your-own-scaffolding-plugin-for-cakephp2"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/how-to-create-your-own-scaffolding-plugin-for-cakephp2"><meta itemprop="datePublished" content="2012-02-24T12:05:46+00:00"><meta itemprop="dateModified" content="2012-02-26T08:46:48+00:00"><meta itemprop="dateCreated" content="2012-02-24T10:00:33+00:00"><meta itemprop="keywords" content="CakePHP,CakePHP 2.0,CakePHP 2.1,CakePHP 2.x,php,Plugin,Scaffolding,TreeBehavior"><meta itemprop="wordCount" content="894"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/how-to-create-your-own-scaffolding-plugin-for-cakephp2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CsvExport Behavior for CakePHP 2</title>
		<link>http://www.pronique.com/blog/csvexport-behavior-for-cakephp-2?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=csvexport-behavior-for-cakephp-2</link>
		<comments>http://www.pronique.com/blog/csvexport-behavior-for-cakephp-2#comments</comments>
		<pubDate>Mon, 20 Feb 2012 23:54:32 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[CakePHP 2.0]]></category>
		<category><![CDATA[CakePHP 2.1]]></category>
		<category><![CDATA[CakePHP 2.x]]></category>
		<category><![CDATA[csv export]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1849</guid>
		<description><![CDATA[This behavior is the exact opposite of the CsvImport behavior included in the Utils plugin from CakeDC. This Behavior will<a href="http://www.pronique.com/blog/csvexport-behavior-for-cakephp-2" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p>This behavior is the exact opposite of the CsvImport behavior included in the Utils plugin from CakeDC.  This Behavior will end on github but for now, here is the source code and instructions on how to use the Behavior.<span id="more-1849"></span></p>
<h3>Installation</h3>
<p>Copy the <a href="#CsvExportBehavior">below</a> source code into <span class="path">app/Model/Behavior/CsvExportBehavior.php</span>.</p>
<h3>Behavior Usage</h3>
<p>Enable the behavior inside your Model(s), if you would like csv export functionality add the behavior to AppModel.</p>
<pre class="brush: php">
public $actsAs = array(
    'CsvExport'
);
</pre>
<p>The following shows all of the available configuration options along with their default values.</p>
<pre class="brush: php">
public $actsAs = array(
    'CsvExport' => array(
        'delimiter'  => ';', //The delimiter for the values, default is ;
        'enclosure' => '"', //The enclosure, default is "
        'max_execution_time' => 360, //Increase for Models with lots of data, has no effect is php safemode is enabled.
        'encoding' => 'utf8' //Prefixes the return file with a BOM and attempts to utf_encode() data
    )
);
</pre>
<h3>Using the Behavior from your Controller</h3>
<p>The following controller action will prompt to download a csv formatted file containing all of the records in your Model&#8217;s underlying database table.  If you would like this export functionality for all controllers in your application add this method to AppController.</p>
<pre class="brush: php">
public function export() {
    $this->autoRender = false;
    $modelClass = $this->modelClass;
    $this->response->type('Content-Type: text/csv');
    $this->response->download( strtolower( Inflector::pluralize( $modelClass ) ) . '.csv' );
    $this->response->body( $this->$modelClass->exportCSV() );
}
</pre>
<h3 id="CsvExportBehavior">CsvExportBehavior.php</h3>
<pre class="brush: php">
&lt;?php
/**
 * Copyright 2011, PRONIQUE Software (http://www.pronique.com)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright Copyright 2011, PRONIQUE Software (http://www.pronique.com)
 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
 */

/**
 *
 *
 * Csv Export Behavior
 *
 * @subpackage models.behaviors
 */
class CsvExportBehavior extends ModelBehavior {

/**
 * Exportable behavior settings
 *
 * @var array
 */
	public $settings = array();

/**
 * List of errors generated by the export action
 *
 * @var array
 */
	public $errors = array();

/**
 * List of objects instances or callables to notify from events on this class
 *
 * @var array
 */
	protected $_subscribers = array();

    protected $tmpDir = 'csvexport';
/**
 * Initializes this behavior for the model $Model
 *
 * @param Model $Model
 * @param array $settigs list of settings to be used for this model
 * @return void
 */
	public function setup(Model &#038;$Model, $settings = array()) {
		if (!isset($this->settings[$Model->alias])) {
			$this->settings[$Model->alias] = array(
                'encoding' => 'utf8',
				'delimiter' => ';',
				'enclosure' => '"',
				'max_execution_time' => 360
			);
		}
		$this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings);
	}

/**
 * Returns a csv formatted string of every record in the model
 *
 * @param Model $Model
 * @return string the csv data
 */
    public function exportCSV(Model &#038;$Model ) {
        if( !ini_get('safe_mode') &#038;&#038; ini_get('max_execution_time') < $this->settings[$Model->alias]['max_execution_time']  ){
            set_time_limit($this->settings[$Model->alias]['max_execution_time']); //Extend timout to 6 minutes for large data exports.
        } 

        $Model->recursive = -1;
        $records = $Model->find('all');
        if ( !empty($records ) ) {
            $this->ensureTmp();

            $tmpFilename = TMP . $this->tmpDir . DS .  strtolower( Inflector::pluralize($Model->alias) ) . '-' . date('Ymd-Hms') . '.csv';
            $fp = fopen($tmpFilename, 'w');
            if ( $this->settings[$Model->alias]['encoding'] == 'utf8' ) {
                fwrite($fp, "\xEF\xBB\xBF");
                fputcsv( $fp, $this->arrayToUtf8( array_keys($records[0][$Model->alias]) ), $this->settings[$Model->alias]['delimiter'], $this->settings[$Model->alias]['enclosure'] );
            } else {
                fputcsv( $fp, array_keys($records[0][$Model->alias]), $this->settings[$Model->alias]['delimiter'], $this->settings[$Model->alias]['enclosure'] );
            }

            foreach( $records as $record ) {
                if ( $this->settings[$Model->alias]['encoding'] == 'utf8' ) {
                    fputcsv($fp, $this->arrayToUtf8( $record[$Model->alias] ), $this->settings[$Model->alias]['delimiter'], $this->settings[$Model->alias]['enclosure'] );
                } else {
                    fputcsv($fp, $record[$Model->alias], $this->settings[$Model->alias]['delimiter'], $this->settings[$Model->alias]['enclosure'] );
                }
            }
            fclose( $fp );
            $data = file_get_contents( $tmpFilename );
            $this->cleanupTmp( $tmpFilename );
            return $data;
        }
        return false;
    }

/**
 * Returns the errors generated by last export
 *
 * @param Model $Model
 * @return array
 */
	public function getExportErrors(Model &#038;$Model) {
		if (empty($this->errors[$Model->alias])) {
			return array();
		}
		return $this->errors[$Model->alias];
	}

/**
 * Attachs a new listener for the events generated by this class
 *
 * @param Model $Model
 * @param mixed listener instances of an object or valid php callback
 * @return void
 */
	public function attachExportListener(Model $Model, $listener) {
		$this->_subscribers[$Model->alias][] = $listener;
	}

/**
 * Notifies the listeners of events generated by this class
 *
 * @param Model $Model
 * @param string $action the name of the event. It will be used as method name for object listeners
 * @param mixed $data additional information to pass to the listener callback
 * @return void
 */
	protected function _notify(Model $Model, $action, $data = null) {
		if (empty($this->_subscribers[$Model->alias])) {
			return;
		}
		foreach ($this->_subscribers[$Model->alias] as $object) {
			if (method_exists($object, $action)) {
				$object->{$action}($data);
			}
			if (is_callable($object)) {
				call_user_func($object, $action, $data);
			}
		}
	}

    /**
    * This Behavior writes tmp files to take advantage of the built-in fputcsv function.
    *
    */
    protected function ensureTmp() {
        $tmpDir = TMP . $this->tmpDir;
        if ( !file_exists($tmpDir ) ) {
            mkdir( $tmpDir, 0777);
        }
    }
    /**
    * Delete the tmp file, only if $tmp_file lives in TMP directory
    * otherwise throw an Exception
    *
    * @param mixed $tmp_file
    */
    protected function cleanupTmp( $tmp_file='' ) {
        $realpath = realpath( $tmp_file );

        if ( substr( $realpath, 0, strlen( TMP ) ) != TMP ) {
            throw new Exception('I refuse to delete a file outside of ' . TMP );
        }

        if ( file_exists( $tmp_file ) ) {
            unlink( $tmp_file );
        }
    }

    /**
    * if array is not UTF-8 then convert keys and values to UTF-8
    * method is recursive
    *
    * @param mixed $in
    */
    protected function arrayToUtf8($in) {
        if (is_array($in)) {
            foreach ($in as $key => $value) {
                $out[$this->arrayToUtf8($key)] = $this->arrayToUtf8($value);
            }
        } elseif(is_string($in)) {
            if(mb_detect_encoding($in) != "UTF-8")
                return utf8_encode($in);
            else
                return $in;
        } else {
            return $in;
        }
        return $out;
    }
}
</pre>
<h3>License</h3>
<p>Licensed under The MIT License (http://www.opensource.org/licenses/mit-license.php)</p>
<h4>Keywords:</h4><ul><li>cakephp csv export</li><li>cakephp 2 rest</li><li>cakephp csv</li><li>cakephp 2 0 csv</li><li>import csv to database using cakephp</li><li>how to export all data using csv file in cakephp</li><li>cakephp export to csv</li><li>cakephp export csv behaviour</li><li>importing data from csv into cakephp</li><li>csv export cakephp 2 0</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/csvexport-behavior-for-cakephp-2"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/csvexport-behavior-for-cakephp-2"><meta itemprop="datePublished" content="2012-02-20T17:54:32+00:00"><meta itemprop="dateModified" content="2012-02-26T09:04:12+00:00"><meta itemprop="dateCreated" content="2012-02-20T17:33:41+00:00"><meta itemprop="keywords" content="CakePHP,CakePHP 2.0,CakePHP 2.1,CakePHP 2.x,csv export,php"><meta itemprop="wordCount" content="924"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/csvexport-behavior-for-cakephp-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Enable CSV Import for all controllers/models in a CakePHP 2.x project</title>
		<link>http://www.pronique.com/blog/enable-csv-import-all-controllers-models-cakephp-2?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=enable-csv-import-all-controllers-models-cakephp-2</link>
		<comments>http://www.pronique.com/blog/enable-csv-import-all-controllers-models-cakephp-2#comments</comments>
		<pubDate>Mon, 20 Feb 2012 19:55:48 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[CakePHP 2.0]]></category>
		<category><![CDATA[CakePHP 2.1]]></category>
		<category><![CDATA[CakePHP 2.x]]></category>
		<category><![CDATA[csv import]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1810</guid>
		<description><![CDATA[I often use phpMyAdmin to import csv data into projects I am building, this can be tedious as phpMyAdmin requires<a href="http://www.pronique.com/blog/enable-csv-import-all-controllers-models-cakephp-2" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p><img itemprop="image" src="http://www.pronique.com/wp-content/uploads/2012/02/cakephp-2-import.png" alt="CakePHP Logo" title="CakePHP 2" width="125" height="125" class="alignright size-full wp-image-1844" />I often use phpMyAdmin to import csv data into projects I am building, this can be tedious as phpMyAdmin requires the number of fields and field order to match exactly for the csv import to work.  </p>
<p>This frustration prompted me to explore the <span id="more-1810"></span><a rel="external nofollow" href="http://github.com/cakedc/utils">Utils plugin</a> by CakeDC which contains a <a rel="external nofollow" href="https://github.com/CakeDC/utils/blob/2.0/Model/Behavior/CsvImportBehavior.php">CsvImport behavior</a>.  I wanted this functionality for all of my controllers/models during the development process.  Import functionality will eventually end up under the admin interface of my project.  If you use admin routing and would like the import functionality only available through an admin url simply change the method name below to admin_import().</p>
<h3>Install the CakeDC Utils plugin</h3>
<p>Using git, clone the Utils Plugin Repo and checkout the 2.0 branch.</p>
<pre class="cli">
sudo git clone https://github.com/CakeDC/utils.git app/Plugin/Utils
cd app/Plugin/Utils
sudo git checkout 2.0
</pre>
<h3>Enable the Utils plugin</h3>
<p>Add the following line to <span class="path">app/Config/bootstrap.php</span> to enable the plugin.</p>
<pre class="brush: php">
CakePlugin::load('Utils');
</pre>
<h3>Enable the CsvImport behavior in AppModel</h3>
<p>Add the following code to <span class="path">app/Model/AppModel.php</span>.  If you only want csv import capability for a specific controller, add it there.</p>
<pre class="brush: php">
public $actsAs = array(
    'Utils.CsvImport' => array(
        'delimiter'  => ','
    )
);
</pre>
<h3>Create a shared View called Common</h3>
<p>Create the directory <span class="path">app/View/Common</span> and add the shared <span class="path">import.ctp</span> view</p>
<pre class="cli">
mkdir app/View/Common
touch app/View/Common/import.csv
</pre>
<pre class="brush: php">
<h3>Import &lt;?php echo Inflector::pluralize($modelClass);?> from CSV data file</h3>

&lt;?php
    echo $this->Form->create($modelClass, array('action' => 'import', 'type' => 'file') );
    echo $this->Form->input('CsvFile', array('label'=>'','type'=>'file') );
    echo $this->Form->end('Submit');
?&gt;
</pre>
<h3>Add the import action to AppController</h3>
<p>Paste the following code into <span class="path">app/Controller/AppController.php</span>.</p>
<pre class="brush: php">
/**
* CSV Import functionality for all controllers
*
*/
function import() {
    $modelClass = $this->modelClass;
    if ( $this->request->is('POST') ) {
        $records_count = $this->$modelClass->find( 'count' );
        try {
            $this->$modelClass->importCSV( $this->request->data[$modelClass]['CsvFile']['tmp_name']  );
        } catch (Exception $e) {
            $import_errors = $this->$modelClass->getImportErrors();
            $this->set( 'import_errors', $import_errors );
            $this->Session->setFlash( __('Error Importing') . ' ' . $this->request->data[$modelClass]['CsvFile']['name'] . ', ' . __('column name mismatch.')  );
            $this->redirect( array('action'=>'import') );
        }

        $new_records_count = $this->$modelClass->find( 'count' ) - $records_count;
        $this->Session->setFlash(__('Successfully imported') . ' ' . $new_records_count .  ' records from ' . $this->request->data[$modelClass]['CsvFile']['name'] );
        $this->redirect( array('action'=>'index') );
    }
    $this->set('modelClass', $modelClass );
    $this->render('../Common/import');
} //end import()
</pre>
<h3>Add an Import link to the Scaffolding View</h3>
<p>If you are using scaffoling in your project and would like to add a link to the<br />
import view, copy <span class="path">lib/Cake/View/Scaffold/index.ctp</span> to <span class="path">app/View/Scaffold/index.ctp</span><br />
and add the following after line 98.</p>
<pre class="brush: php">
echo "&lt;li&gt;" . $this->Html->link(
    __d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))),
    array('controller' => $_details['controller'], 'action' => 'add')
) . "&lt;/li&gt;";
</pre>
<p>If you choose not to customize the scaffolding view then you can point your browser directly to the controller action, <span class="url">http://domain.com/controller_name/import</span.</p>
<p>&nbsp;</p>
<h4>Keywords:</h4><ul><li>cakephp import csv</li><li>cake read csv</li><li>cakephp csv import</li><li>cakephp upload csv file</li><li>cakephp 2 o csv</li><li>cakephp change input form type scaffold</li><li>cakephp core git</li><li>csv cakephp 2 0</li><li>import csv using cakephp form</li><li>cakeplugin::load(utils);</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/enable-csv-import-all-controllers-models-cakephp-2"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/enable-csv-import-all-controllers-models-cakephp-2"><meta itemprop="datePublished" content="2012-02-20T13:55:48+00:00"><meta itemprop="dateModified" content="2012-02-26T09:05:09+00:00"><meta itemprop="dateCreated" content="2012-02-20T13:11:55+00:00"><meta itemprop="keywords" content="CakePHP,CakePHP 2.0,CakePHP 2.1,CakePHP 2.x,csv import,git"><meta itemprop="wordCount" content="495"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/enable-csv-import-all-controllers-models-cakephp-2/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[CakePHP] Modeling a simple accounting system with estimates, invoices, and payments.</title>
		<link>http://www.pronique.com/blog/cakephp-modeling-a-simple-accounting-system-with-estimates-invoices-and-payments?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cakephp-modeling-a-simple-accounting-system-with-estimates-invoices-and-payments</link>
		<comments>http://www.pronique.com/blog/cakephp-modeling-a-simple-accounting-system-with-estimates-invoices-and-payments#comments</comments>
		<pubDate>Fri, 17 Feb 2012 21:33:12 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[CakePHP 2.0]]></category>
		<category><![CDATA[CakePHP 2.1]]></category>
		<category><![CDATA[CakePHP 2.x]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1798</guid>
		<description><![CDATA[Database tables and Model classes are where most CakePHP applications begin. With this example, we will focus on Model associations<a href="http://www.pronique.com/blog/cakephp-modeling-a-simple-accounting-system-with-estimates-invoices-and-payments" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p>Database tables and Model classes are where most CakePHP applications begin.  With this example, we will focus on Model associations rather than providing a complete tutorial of an application.  Consider this a solid foundation for building an Accounting Web App, this is the M in MVC.</p>
<p>The idea behind what we are modeling today is a simple accounting web app similar to Freshbooks or something QuickBooks offers. The model definitions and relationships are as follows.<span id="more-1798"></span></p>
<p><strong>Company</strong> &#8211; Represents the provider company within the accounting system.  A Company has many Groups, Users, and Clients.  Additionally, a Company has many Items, Estimates, Invoices, and Payments.</p>
<p><strong>User</strong> &#8211; Represents an employee of the Company.  A User belongs to a Company and many Groups through GroupMembership.  As well, User are who will login and use the application.</p>
<p><strong>Client</strong> &#8211; Represents the paying organization that is your customer. A Client has many contacts, estimates, invoices, and payments.</p>
<p><strong>Contact</strong> &#8211; Represents an employee of a Client Company.  A Contact belongs to a Client.</p>
<p><strong>Estimate</strong> &#8211; Has Many EstimateItems and belongs to both a Company(Issuer) and a Client(Recipent).</p>
<p><strong>Invoice</strong> &#8211; Has Many InvoiceItems and  Payments.  Belongs to both a Company(Issuer) and a Client(Recipent).</p>
<p><strong>Payment</strong> &#8211; Belongs to both an Invoice, Client(Payor) and a Company(Payee).</p>
<h3>Client.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Client extends AppModel {
    public $displayField = 'name';

    public $hasMany = array('Invoice', 'Payment', 'Estimate' ,'Contact' );

    public $belongsTo = array( 'Company' );

}
</pre>
<h3>Company.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Company extends AppModel {

    public $hasMany = array('Client','User','Group' );

}
</pre>
<h3>Contact.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Contact extends AppModel {
    public $displayField = 'last_name';

    public $belongsTo = array( 'Client' );

}
</pre>
<h3>Estimate.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Estimate extends AppModel {
    public $displayField = 'number';

    public $hasMany = array('EstimateItem' );

    public $belongsTo = array( 'Company', 'Client', 'TaxRate' );

}
</pre>
<h3>EstimateItem.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class EstimateItem extends AppModel {
    public $displayField = 'item';

    public $belongsTo = array( 'Estimate' );

}
</pre>
<h3>Group.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Group extends AppModel {

    public $hasMany = array(
        'Children'=>array(
            'className'=>'Group',
            'foreignKey'=>'parent_id',
            'order' => 'parent_id ASC'
        ),
        'GroupMembership'
    );
    public $belongsTo = array(
        'Company',
        'Parent'=>array(
            'className'=>'Group',
            'foreignKey'=>'parent_id'
        )                             

    );

}
</pre>
<h3>GroupMembership.php</h3>
<pre class="brush: php">
&lt;?php
App::uses( 'AppModel', 'Model' );

class GroupMembership extends AppModel {
    public $belongsTo = array(
        'User', 'Group'
    );
}
</pre>
<h3>Industry.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Industry extends AppModel {
    public $displayField = 'name';
}
</pre>
<h3>Invoice.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Invoice extends AppModel {
    public $displayField = 'number';

    public $hasMany = array('InvoiceItem', 'Payment' );

    public $belongsTo = array( 'Company', 'Client', 'TaxRate' );

}
</pre>
<h3>InvoiceItem.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class InvoiceItem extends AppModel {
    public $displayField = 'item';

    public $belongsTo = array( 'Invoice' );

}
</pre>
<h3>Item.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Item extends AppModel {
    public $displayField = 'item';

    public $belongsTo = array('company');
}
</pre>
<h3>Payment.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class Payment extends AppModel {
    public $displayField = 'number';

    public $belongsTo = array( 'Company','Invoice', 'Client', 'PaymentMethod' );

}
</pre>
<h3>TaxRate.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class TaxRate extends AppModel {
    var $belongsTo = array('Company');

}
</pre>
<h3>User.php</h3>
<pre class="brush: php">
&lt;?php
App::uses('AppModel', 'Model');

class User extends AppModel {
    public $displayField = 'email';

    public $belongsTo = array('Company');
    public $hasMany = array('GroupMembership');

}
</pre>
<h3>Database Schema</h3>
<p>Below is the schema file generated using the CakePHP console command &#8216;cake schema generate&#8217;.</p>
<pre class="brush: php">
&lt;?php
class AppSchema extends CakeSchema {

	public $file = 'cakebilling.php';

	public function before($event = array()) {
		return true;
	}

	public function after($event = array()) {
	}

	public $clients = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'number' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 23, 'key' => 'unique', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'country' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 3, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'street_1' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'street_2' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'city' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'state' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'postal_code' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 10, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'industry' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'company_size' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 12, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'business_phone' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 20, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'fax' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 20, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'invoice_preference' => array('type' => 'string', 'null' => false, 'default' => 'email', 'length' => 10, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'internal_notes' => array('type' => 'text', 'null' => true, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'number' => array('column' => 'number', 'unique' => 1), 'company_id' => array('column' => 'company_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $companies = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'profession' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'currency' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 3, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'country' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 3, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'street_1' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'street_2' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'city' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'state' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'postal_code' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 10, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 128, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'business_phone' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 24, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'fax' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 24, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'mobile' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 24, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'account_url' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'next_estimate' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 23, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'next_invoice' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 23, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'next_payment' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 23, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'timezone_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 5),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $contacts = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'first_name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'last_name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 128, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'phone' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 24, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'mobile' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 24, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'client_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'client_id' => array('column' => 'client_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $estimate_items = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'quantity' => array('type' => 'integer', 'null' => false, 'default' => NULL),
		'item' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'description' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'unit_cost' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'line_total' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'order' => array('type' => 'integer', 'null' => false, 'default' => '10', 'length' => 10),
		'estimate_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'invoice_id' => array('column' => 'estimate_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $estimates = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'number' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 16, 'key' => 'unique', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'notes' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'terms' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'subtotal' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'total' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'amount_paid' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'tax_rate' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '5,3'),
		'tax_rate_id' => array('type' => 'integer', 'null' => true, 'default' => NULL, 'length' => 10),
		'client_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'number' => array('column' => 'number', 'unique' => 1), 'client_id' => array('column' => 'client_id', 'unique' => 0), 'company_id' => array('column' => 'company_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $group_memberships = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'group_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10),
		'user_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $groups = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 32, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'parent_id' => array('type' => 'integer', 'null' => true, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'company_id' => array('column' => 'company_id', 'unique' => 0), 'parent_id' => array('column' => 'parent_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $industries = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $invoice_items = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'quantity' => array('type' => 'integer', 'null' => false, 'default' => NULL),
		'item' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'description' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'unit_cost' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'line_total' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'order' => array('type' => 'integer', 'null' => false, 'default' => '10', 'length' => 10),
		'invoice_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'invoice_id' => array('column' => 'invoice_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $invoices = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'number' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 16, 'key' => 'unique', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'notes' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'terms' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'subtotal' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'total' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'amount_paid' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'tax_rate' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '5,3'),
		'tax_rate_id' => array('type' => 'integer', 'null' => true, 'default' => NULL, 'length' => 10),
		'client_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'number' => array('column' => 'number', 'unique' => 1), 'client_id' => array('column' => 'client_id', 'unique' => 0), 'company_id' => array('column' => 'company_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $items = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'item' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'key' => 'unique', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'description' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'cost' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'price' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'msrp' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'stock' => array('type' => 'integer', 'null' => true, 'default' => NULL, 'length' => 10),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'item' => array('column' => 'item', 'unique' => 1), 'company_id' => array('column' => 'company_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $payments = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'number' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 23, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'client_name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'date' => array('type' => 'date', 'null' => true, 'default' => NULL),
		'currency' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 3, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'amount' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '8,2'),
		'reference' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 23, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'notes' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'client_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'payment_method_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10),
		'invoice_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'invoice_id' => array('column' => 'invoice_id', 'unique' => 0), 'client_id' => array('column' => 'client_id', 'unique' => 0), 'company_id' => array('column' => 'company_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $tax_rates = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 32, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'rate' => array('type' => 'float', 'null' => false, 'default' => NULL, 'length' => '5,3'),
		'number' => array('type' => 'integer', 'null' => false, 'default' => NULL),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'company_id' => array('column' => 'company_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
	public $users = array(
		'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
		'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 128, 'key' => 'unique', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'username' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 128, 'key' => 'unique', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'first_name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'last_name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
		'company_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'index'),
		'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'updated' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'email' => array('column' => 'email', 'unique' => 1), 'username' => array('column' => 'username', 'unique' => 1), 'company_id' => array('column' => 'company_id', 'unique' => 0)),
		'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
	);
}
</pre>
<h3>MySQL Data Dump</h3>
<div class="note">Note: In the SQL below the table names have a prefix of pro_, you will need to either change your app/Config/database.php to use this extension or open the sql in your favorite text editor and search and replace &#8216;pro_&#8217;.</div>
<pre class="brush: sql">
--
-- Table structure for table `pro_audit_logs`
--

CREATE TABLE IF NOT EXISTS `pro_audit_logs` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `action` varchar(12) NOT NULL,
  `detail` text NOT NULL,
  `model` varchar(23) NOT NULL,
  `foreign_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fkey` (`model`,`foreign_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_clients`
--

CREATE TABLE IF NOT EXISTS `pro_clients` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `number` varchar(23) NOT NULL,
  `name` varchar(64) NOT NULL,
  `country` char(3) NOT NULL,
  `street_1` varchar(64) NOT NULL,
  `street_2` varchar(64) DEFAULT NULL,
  `city` varchar(64) NOT NULL,
  `state` varchar(64) NOT NULL,
  `postal_code` varchar(10) NOT NULL,
  `industry` varchar(64) DEFAULT NULL,
  `company_size` varchar(12) DEFAULT NULL,
  `business_phone` varchar(20) DEFAULT NULL,
  `fax` varchar(20) DEFAULT NULL,
  `invoice_preference` varchar(10) NOT NULL DEFAULT 'email',
  `internal_notes` text,
  `company_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `number` (`number`),
  KEY `company_id` (`company_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_companies`
--

CREATE TABLE IF NOT EXISTS `pro_companies` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  `profession` varchar(64) NOT NULL,
  `currency` char(3) NOT NULL,
  `country` char(3) NOT NULL,
  `street_1` varchar(64) NOT NULL,
  `street_2` varchar(64) NOT NULL,
  `city` varchar(64) NOT NULL,
  `state` varchar(64) NOT NULL,
  `postal_code` varchar(10) NOT NULL,
  `email` varchar(128) NOT NULL,
  `business_phone` varchar(24) NOT NULL,
  `fax` varchar(24) NOT NULL,
  `mobile` varchar(24) NOT NULL,
  `account_url` varchar(255) NOT NULL,
  `next_estimate` varchar(23) DEFAULT NULL,
  `next_invoice` varchar(23) DEFAULT NULL,
  `next_payment` varchar(23) DEFAULT NULL,
  `timezone_id` smallint(5) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_contacts`
--

CREATE TABLE IF NOT EXISTS `pro_contacts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(64) NOT NULL,
  `last_name` varchar(64) NOT NULL,
  `email` varchar(128) NOT NULL,
  `phone` varchar(24) NOT NULL,
  `mobile` varchar(24) NOT NULL,
  `client_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `client_id` (`client_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_estimates`
--

CREATE TABLE IF NOT EXISTS `pro_estimates` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `number` varchar(16) NOT NULL,
  `notes` text NOT NULL,
  `terms` text NOT NULL,
  `subtotal` decimal(8,2) NOT NULL,
  `total` decimal(8,2) NOT NULL,
  `amount_paid` decimal(8,2) NOT NULL,
  `tax_rate` decimal(5,3) NOT NULL,
  `tax_rate_id` int(10) unsigned DEFAULT NULL,
  `client_id` int(10) unsigned NOT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `number` (`number`),
  KEY `client_id` (`client_id`),
  KEY `company_id` (`company_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_estimate_items`
--

CREATE TABLE IF NOT EXISTS `pro_estimate_items` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `quantity` int(11) NOT NULL,
  `item` varchar(64) NOT NULL,
  `description` text NOT NULL,
  `unit_cost` decimal(8,2) NOT NULL,
  `line_total` decimal(8,2) NOT NULL,
  `order` int(10) unsigned NOT NULL DEFAULT '10',
  `estimate_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `invoice_id` (`estimate_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_groups`
--

CREATE TABLE IF NOT EXISTS `pro_groups` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `parent_id` int(10) unsigned DEFAULT NULL,
  `company_id` int(11) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `company_id` (`company_id`),
  KEY `parent_id` (`parent_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=9 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_group_memberships`
--

CREATE TABLE IF NOT EXISTS `pro_group_memberships` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` int(10) unsigned NOT NULL,
  `user_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_industries`
--

CREATE TABLE IF NOT EXISTS `pro_industries` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=48 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_invoices`
--

CREATE TABLE IF NOT EXISTS `pro_invoices` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `number` varchar(16) NOT NULL,
  `notes` text NOT NULL,
  `terms` text NOT NULL,
  `subtotal` decimal(8,2) NOT NULL,
  `total` decimal(8,2) NOT NULL,
  `amount_paid` decimal(8,2) NOT NULL,
  `tax_rate` decimal(5,3) NOT NULL,
  `tax_rate_id` int(10) unsigned DEFAULT NULL,
  `client_id` int(10) unsigned NOT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `number` (`number`),
  KEY `client_id` (`client_id`),
  KEY `company_id` (`company_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_invoice_items`
--

CREATE TABLE IF NOT EXISTS `pro_invoice_items` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `quantity` int(11) NOT NULL,
  `item` varchar(64) NOT NULL,
  `description` text NOT NULL,
  `unit_cost` decimal(8,2) NOT NULL,
  `line_total` decimal(8,2) NOT NULL,
  `order` int(10) unsigned NOT NULL DEFAULT '10',
  `invoice_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `invoice_id` (`invoice_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_items`
--

CREATE TABLE IF NOT EXISTS `pro_items` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `item` varchar(64) NOT NULL,
  `description` text NOT NULL,
  `cost` decimal(8,2) NOT NULL,
  `price` decimal(8,2) NOT NULL,
  `msrp` decimal(8,2) NOT NULL,
  `stock` int(10) unsigned DEFAULT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `item` (`item`),
  KEY `company_id` (`company_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_payments`
--

CREATE TABLE IF NOT EXISTS `pro_payments` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `number` varchar(23) NOT NULL,
  `client_name` varchar(64) NOT NULL,
  `date` date DEFAULT NULL,
  `currency` char(3) NOT NULL,
  `amount` decimal(8,2) NOT NULL,
  `reference` varchar(23) NOT NULL,
  `notes` text NOT NULL,
  `client_id` int(10) unsigned NOT NULL,
  `payment_method_id` int(10) unsigned NOT NULL,
  `invoice_id` int(10) unsigned NOT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `invoice_id` (`invoice_id`),
  KEY `client_id` (`client_id`),
  KEY `company_id` (`company_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_payment_methods`
--

CREATE TABLE IF NOT EXISTS `pro_payment_methods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(23) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_tax_rates`
--

CREATE TABLE IF NOT EXISTS `pro_tax_rates` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `rate` decimal(5,3) NOT NULL,
  `number` int(11) NOT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `company_id` (`company_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_timezones`
--

CREATE TABLE IF NOT EXISTS `pro_timezones` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  `offset` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=83 ;

-- --------------------------------------------------------

--
-- Table structure for table `pro_users`
--

CREATE TABLE IF NOT EXISTS `pro_users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(128) NOT NULL,
  `username` varchar(128) NOT NULL,
  `password` varchar(40) NOT NULL,
  `first_name` varchar(64) NOT NULL,
  `last_name` varchar(64) NOT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  UNIQUE KEY `username` (`username`),
  KEY `company_id` (`company_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
</pre>
<h4>Keywords:</h4><ul><li>invoice program in cakephp</li><li>cakephp accounting system</li><li>php accounting cakephp</li><li>cakephp invoice</li><li>invoice cake php</li><li>invocir clients invoices cake php</li><li>how to send invoice to customer in cakephp</li><li>invoice system in cake php</li><li>invoicing cakephp</li><li>php accounting</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/cakephp-modeling-a-simple-accounting-system-with-estimates-invoices-and-payments"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/cakephp-modeling-a-simple-accounting-system-with-estimates-invoices-and-payments"><meta itemprop="datePublished" content="2012-02-17T15:33:12+00:00"><meta itemprop="dateModified" content="2012-02-26T09:08:28+00:00"><meta itemprop="dateCreated" content="2012-02-17T15:16:38+00:00"><meta itemprop="keywords" content="CakePHP,CakePHP 2.0,CakePHP 2.1,CakePHP 2.x"><meta itemprop="wordCount" content="4205"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/cakephp-modeling-a-simple-accounting-system-with-estimates-invoices-and-payments/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Installing CakePHP 2.1 with git clone</title>
		<link>http://www.pronique.com/blog/installing-cakephp-2-1-with-git-clone?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=installing-cakephp-2-1-with-git-clone</link>
		<comments>http://www.pronique.com/blog/installing-cakephp-2-1-with-git-clone#comments</comments>
		<pubDate>Wed, 15 Feb 2012 14:56:49 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[CakePHP 2.1]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1723</guid>
		<description><![CDATA[If you have ssh shell access to you web server installing CakePHP very simple using git. First, determine if you<a href="http://www.pronique.com/blog/installing-cakephp-2-1-with-git-clone" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p>If you have ssh shell access to you web server installing CakePHP very simple using git.<br />
First, determine if you have git installed.<span id="more-1723"></span>  At the command line type</p>
<pre class="cli">
git --help
</pre>
<p>If you get a response like “No command &#8216;git&#8217; found” then you will need to first install git</p>
<pre class="cli">
#debian based systems
sudo apt-get install git

#rpm based systems
sudo yum install git
</pre>
<h3>Clone the CakePHP repository and checkout the latest version</h3>
<pre class="cli">
mkdir myapp
sudo git clone https://github.com/cakephp/cakephp.git myapp
cd myapp
sudo git checkout 2.1
</pre>
<p>The latest version of CakePHP is 2.1.  As new versions are released you will probably want to use the latest.  To get a list of available branches/tags use the the following git commands.</p>
<pre class="cli">
sudo git tag
// or
sudo git branch -a
</pre>
<p>If you see a branch/tag the is newer, switch to it using the checkout command</p>
<h3>Updating to newer versions of CakePHP 2.1 as they are released.</h3>
<p>We will first perform a git pull to refresh our copy of the CakePHP repo. </p>
<pre class="cli">
cd myapp
sudo git pull
</pre>
<p>Next, list the available branches/tags to find the latest version.</p>
<pre class="cli">
sudo git tag
// or
sudo git branch -a
</pre>
<p>Switch the the newer version</p>
<pre class="cli">
sudo git checkout 2.1
</pre>
<h3>Display your current version of CakePHP</h3>
<pre class="cli">
cat myapp/lib/Cake/VERSION.txt
</pre>
<p>or </p>
<pre class="cli">
sudo git describe --always --tag
</pre>
<h3>Switching from CakePHP 2.0 to 2.1</h3>
<p>If you have already cloned the CakePHP repo and are using version 2.0 there are some special considerations when switching to 2.1.</p>
<p><span class="path">app/Controller/AppController.php</span> must exist<br />
<span class="path">app/Controller/PagesController.php</span> must exist<br />
<span class="path">app/Model/AppModel.php</span> must exist<br />
<span class="path">app/View/Helpers/AppHelper.php</span> must exist<br />
<span class="path">.htaccess</span> has changed</p>
<p>For more information on Migrating from CakePHP 2.0 to 2.1 see the <a rel="external nofollow" href="http://book.cakephp.org/2.0/en/appendices/2-1-migration-guide.html">2.1 Migration Guide</a><br /></p>
<h4>Keywords:</h4><ul><li>git cakephp</li><li>cakephp2 1 rest</li><li>cakphp 2 1 update</li><li>install cakephp 2 1</li><li>cakephp 2 1</li><li>cakephp and git</li><li>cakephp upgrade 2 0 to 2 1</li><li>cakephp git clone</li><li>cakephp git</li><li>create cakephp project from the command line</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/installing-cakephp-2-1-with-git-clone"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/installing-cakephp-2-1-with-git-clone"><meta itemprop="datePublished" content="2012-02-15T08:56:49+00:00"><meta itemprop="dateModified" content="2012-02-26T09:13:48+00:00"><meta itemprop="dateCreated" content="2012-02-15T08:53:22+00:00"><meta itemprop="keywords" content="CakePHP,CakePHP 2.1,git"><meta itemprop="wordCount" content="306"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/installing-cakephp-2-1-with-git-clone/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Install and reuse CakePHP across multiple projects</title>
		<link>http://www.pronique.com/blog/install-and-reuse-cakephp-across-multiple-projects?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=install-and-reuse-cakephp-across-multiple-projects</link>
		<comments>http://www.pronique.com/blog/install-and-reuse-cakephp-across-multiple-projects#comments</comments>
		<pubDate>Sat, 11 Feb 2012 18:11:10 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[CakePHP 1.3]]></category>
		<category><![CDATA[CakePHP 2.0]]></category>
		<category><![CDATA[CakePHP 2.1]]></category>
		<category><![CDATA[CakePHP 2.x]]></category>
		<category><![CDATA[CAKE_CORE_INCLUDE_PATH]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1635</guid>
		<description><![CDATA[Update: I have created a command line tool that provides the functionality explained in detail below. Check-out CakeCore and learn<a href="http://www.pronique.com/blog/install-and-reuse-cakephp-across-multiple-projects" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p class="note"><strong>Update</strong>:  I have created a command line tool that provides the functionality explained in detail below.  Check-out <a href="http://www.pronique.com/software/cakecore">CakeCore</a> and learn more.</p>
<p><img itemprop="image" src="http://www.pronique.com/wp-content/uploads/2012/02/cakephp_logo_125_trans.png" alt="" title="CakePHP" width="125" height="125" class="alignright size-full wp-image-1704" />How to maintain one copy of each major version of the CakePHP Core library.  Making upgrades to new versions of the framework effortless even across many applications.   </p>
<p>I use CakePHP for the majority of web-based applications I create.  Before developing this convention I would end up with many redundant copies of the Core library floating around taking up disk space, and making upgrade tedious.<span id="more-1635"></span></p>
<p>To overcome this dilemma, CakePHP allows you to store the core library outside the project and point to its new location by uncommenting the constant <span class="constant">CAKE_CORE_INCLUDE_PATH</span> in <span class="path">app/webroot/index.php</span>.  This means you can have one copy of the core library for any number of CakePHP applications.</p>
<p>CakePHP can be installed using your distributions package management tool apt-get or yum.  The problem with this approach is the version is always several releases behind the current stable build.  To overcome this annoyance, I use the following convention to maintain multiple version of the CakePHP core library on all of my servers.</p>
<h3>Install and Update the CakePHP Core Library</h3>
<p>The logical place for CakePHP to live on my systems is <span class="path">/usr/lib/cakephp</span>, along side Pear and other libraries.  I also maintain multiple versions of the framework as I have 1.3 projects and many 2.x projects.  </p>
<h3>Create the following directory structure to separate each version.</h3>
<pre class="cli">
/usr/lib/cakephp/
/usr/lib/cakephp/1.2/
/usr/lib/cakephp/1.3/
/usr/lib/cakephp/2.0/
/usr/lib/cakephp/2.1/
</pre>
<h3>Use git to install and checkout each version of the framework</h3>
<pre class="cli">
cd /usr/lib/cakephp/1.3
sudo git clone https://github.com/cakephp/cakephp.git /usr/lib/cakephp/1.3
sudo git checkout 1.3

cd /usr/lib/cakephp/2.0
sudo git clone https://github.com/cakephp/cakephp.git /usr/lib/cakephp/2.0
sudo git checkout 2.0

cd /usr/lib/cakephp/2.1
sudo git clone https://github.com/cakephp/cakephp.git /usr/lib/cakephp/2.1
sudo git checkout 2.1
</pre>
<h3>When the CakePHP team releases new builds update each version using git</h3>
<pre class="cli">
cd /usr/lib/cakephp/1.3
sudo git pull

cd /usr/lib/cakephp/2.0
sudo git pull

cd /usr/lib/cakephp/2.1
sudo git pull

cd /usr/lib/cakephp/2.1
sudo git pull
</pre>
<h3>Configure each of your projects to use the Core library</h3>
<p><strong>For CakePHP 2.1 applications</strong>, edit <span class="path">app/webroot/index.php</span>, uncomment and change line 59.  Once tested, you can delete the <span class="path">lib/Cake/</span> directory from your project.</p>
<pre class="brush: php">
define('CAKE_CORE_INCLUDE_PATH', DS . 'usr' . DS . 'lib' . DS . 'cakephp' . DS . '2.1'  . DS . 'lib');
</pre>
<p><strong>For CakePHP 1.3 applications</strong>, edit <span class="path">app/webroot/index.php</span> and change the following around line 53.  Once tested, you can delete the <span class="path">cake/</span> directory from your project.</p>
<pre class="brush: php">
if (!defined('CAKE_CORE_INCLUDE_PATH')) {
    define('CAKE_CORE_INCLUDE_PATH', DS . 'usr' . DS . 'lib' . DS . 'cakephp' . DS . '1.3'  . DS . 'cake' );
}
</pre>
<p>Install and maintain CakePHP into the same location (/usr/lib/cakephp) on all development, testing, and production machines.  Your application will always just work and you will also be storing much less in your code repos!</p>
<p class="note"><strong>Update</strong>:  I have created a command line tool that provides the functionality explained above.  Check-out <a href="http://www.pronique.com/software/cakecore">CakeCore</a> and learn more.</p></p>
<h4>Keywords:</h4><ul><li>upgrade cakephp git pull</li><li>cakephp upgrade git pull</li><li>cakephp 2 1 project to study</li><li>git import cakephp into project</li><li>using cakephp with multiple apps</li><li>how to create installable plugin in cakephp</li><li>how copy installed software for reuse</li><li>have one installation of cakephp</li><li>github cakephp 1 3</li><li>git cakephp project</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/install-and-reuse-cakephp-across-multiple-projects"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/install-and-reuse-cakephp-across-multiple-projects"><meta itemprop="datePublished" content="2012-02-11T12:11:10+00:00"><meta itemprop="dateModified" content="2012-02-26T09:15:46+00:00"><meta itemprop="dateCreated" content="2012-02-11T11:20:24+00:00"><meta itemprop="keywords" content="CakePHP,CakePHP 1.3,CakePHP 2.0,CakePHP 2.1,CakePHP 2.x,CAKE_CORE_INCLUDE_PATH,git,php"><meta itemprop="wordCount" content="554"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/install-and-reuse-cakephp-across-multiple-projects/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Installing MongoDB on Windows the WAMP way</title>
		<link>http://www.pronique.com/blog/installing-mongodb-on-windows-the-wamp-way?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=installing-mongodb-on-windows-the-wamp-way</link>
		<comments>http://www.pronique.com/blog/installing-mongodb-on-windows-the-wamp-way#comments</comments>
		<pubDate>Thu, 09 Feb 2012 18:12:12 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[mongo]]></category>
		<category><![CDATA[mongodb]]></category>
		<category><![CDATA[wamp]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1599</guid>
		<description><![CDATA[This guide is geared towards web developers using wamp. As you know, wamp keeps your filesystem tidy by installing apache,<a href="http://www.pronique.com/blog/installing-mongodb-on-windows-the-wamp-way" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p><img itemprop="image" src="http://www.pronique.com/wp-content/uploads/2012/02/wAMP.png" alt="" title="WAMPServer" width="100" height="100" class="alignright size-full wp-image-1710" />This guide is geared towards web developers using wamp.  As you know, wamp keeps your filesystem tidy by installing apache, mysql, and php all inside <span class="path">c:\wamp</span>.  My setup is a Windows 7 64bit machine running wamp 32bit (Why run wamp 32bit on Windows 7 64bit?).  Although untested on each, this guide should also work for Windows XP, Vista, and Windows Server variants.</p>
<p><img itemprop="image" src="http://www.pronique.com/wp-content/uploads/2012/02/mongodb-logo.png" alt="" title="MongoDB on Windows" width="300" height="100" class="alignright size-full wp-image-1707" />MongoDB is a open source, high performance,NOSQL database server(mongod.exe) and shell(mongo.exe) developed by 10gen.  MongoDB win32 binaries are available in both 64bit and 32bit flavors.  The win32 binaries are packaged as a very simple zip file containing only the binary executables, no installer, and no example config files.</p>
<p>Lets get started! We will install mongodb into the <span class="path">c:\wamp</span> directory, create conf,data,log directories, and configure mongod to run as a windows service.<span id="more-1599"></span></p>
<p>1. Download the latest win32 build of mongodb, choosing your architecture (32bit or 64bit) from <a href="http://www.mongodb.org/downloads" title="http://www.mongodb.org/downloads">http://www.mongodb.org/downloads</a>.</p>
<p>2. Create the directory <span class="path">c:\wamp\bin\mongodb\</span> and extract the contents of the zip archive here.  Your directory structure should look something like the following depending on the version number and architecture you download.<br />
<span class="path">C:\wamp\bin\mongodb\mongodb-win32-x86_64-2.0.2\</span></p>
<p>3. Create the following directories</p>
<pre class="cli">
mkdir c:\wamp\bin\mongodb\mongodb-win32...2.x.x\data
mkdir c:\wamp\bin\mongodb\mongodb-win32...2.x.x\data\db
mkdir c:\wamp\bin\mongodb\mongodb-win32...2.x.x\logs
mkdir c:\wamp\bin\mongodb\mongodb-win32...2.x.x\conf
</pre>
<p>4. Create the file <span class="path">c:\wamp\bin\mongodb\mongodb-win32&#8230;2.x.x\conf\mongodb.conf</span> and add the following base configuration.</p>
<pre class="code">
# mongodb.conf

# data lives here
dbpath=C:\wamp\bin\mongodb\mongodb-win32...2.x.x\data\db

# where to log
logpath=C:\wamp\bin\mongodb\mongodb-win32...2.x.x\logs\mongodb.log
logappend=true

# only run on localhost for development
bind_ip = 127.0.0.1                                                             

port = 27017
rest = true
</pre>
<p>5. Execute the windows console(command line) as administrator and change to the <span class="path">c:\wamp\bin\mongodb\mongodb-win32&#8230;2.x.x\bin</span> directory.</p>
<p>6. Execute the following command to install mongod as a windows service.</p>
<pre class="cli">
mongod.exe --install --config c:\wamp\bin\mongodb\mongodb-win32...2.x.x\conf\mongodb.conf --logpath c:\wamp\bin\mongodb\mongodb-win32...2.x.x\logs\mongodb.log
</pre>
<p>7. Execute <span class="path">services.msc</span> and scroll down to find the Mongo DB service.  Right click on the service and choose start.  From here you can choose the have the service started automatically on boot or change it to manual and you will need to start the service each time.</p>
<p>8. We can confirm that mongod is running by inspecting the log file located at <span class="path">c:\wamp\mongodb\mongodb-win32&#8230;2.x.x\logs\mongodb.log</span></p>
<p class="note">Note: Each time you make changes to <span class="path">mongodb.conf</span>, you will need to restart the Mongo DB service.</p>
<h3>Adding mongo.exe to your path</h3>
<p><span class="path">mongo.exe</span> is the MongoDB shell, it will be very convenient to add the bin directory to your PATH environment variable so that you can simple type mongo at the command line regardless of your current working directory.</p>
<p>1. Right Click on My Computer<br />
2. Choose Properties and Advanced System Settings<br />
3. Click the Environment Variables button<br />
4. Under system variables scroll down and double click on Path.<br />
5. Append the following to the existing Variable value.</p>
<pre class="code">
;C:\wamp\bin\mongodb\mongodb-win32-x86_64-2.0.2\bin
</pre>
<p>Note: The ; (semicolon) at the beginning is the delimiter between each path.  While you are here, it’s a good idea to also add the path to php.exe.</p>
<pre class="code">
;C:\wamp\bin\php\php5.3.9
</pre>
<p class="note">Remember, your version numbers will probably vary slightly so double check the correct path for your machine and revision of wamp.</p>
<h3>Use mongo.exe to confirm everything is up and running</h3>
<pre class="cli">
Start | Run | cmd.exe <enter>
c:\&gt; mongo
MongoDB shell version: 2.0.2
connecting to: test
&gt;
&gt; use test;
switched to db test
&gt; db.test.insert( {"hello":"world"} );
&gt; db.test.find();
{ "_id" : ObjectId("4f33df871c81e6d645a53dd3"), "hello" : "world" }
&gt; exit;</pre>
<h3>Installing the Mongo PHP Extensions (php_mongo.dll)</h3>
<p>1. Download the latest version of the win32 php extension from <a href="https://github.com/mongodb/mongo-php-driver/downloads">https://github.com/mongodb/mongo-php-driver/downloads</a>. For this example I used <span class="path">mongo-1.2.5.zip</span></p>
<p>2. Extract the zip archive find the <span class="path'>php_mongo.dll</span> located in the <span class="path'>mongo-1.2.5-php5.3vc9ts/</span> directory.  The ts in the folder name means thread safe.</p>
<p>3. Copy <span class="path'>php_mongo.dll</span> to <span class="path'>c:\wamp\bin\php\php-5.3.x\ext\</span></p>
<p>4. Edit <span class="path'>c:\wamp\bin\php\php-5.3.x\php.ini</span> and <span class="path">c:\wamp\bin\apache\Apache2.2.xx\bin\php.ini</span> and add the following line near the other loaded extensions.</p>
<pre class="code">extension=php_mongo.dll</pre>
<p>5. Restart all the wamp services by clicking on the wamp task tray icon and choosing ‘Restart All Services’.</p>
<p>6. Open your browser to <a href="http://127.0.0.1/?phpinfo=1" title="http://127.0.0.1/?phpinfo=1">http://127.0.0.1/?phpinfo=1</a> to confirm that the mongo driver is loaded.</p>
<h2>Additional Tips for running MongoDB on Windows</h2>
<h3>Stop and Start the mongod service from the command line</h3>
<p class="note">Note: You must be running the command prompt shell with administrative privileges for the following commands to work.</p>
<p><strong>Stop mongod</strong></p>
<pre class="cli">
c:\> NET STOP "Mongo DB"
</pre>
<p><strong>Start mongod</strong></p>
<pre class="cli">
c:\> NET START "Mongo DB"
</pre>
<p class="hr" />
<h3>Backup your mongodb data files</h3>
<p>1. Stop the &#8220;Mongo DB&#8221; service.</p>
<p>2. Copy <span class="path">c:\wamp\bin\mongodb\mongodb-win32&#8230;2.x.x\data</span> to your backup destination.</p>
<p>3. Restart the &#8220;Mongo DB&#8221; service.</p>
<p class="hr" />
<h3>Why run wamp 32bit on Windows 7 64bit?</h3>
<p>The Mongo PHP driver does not have a 64bit win32 binary,  if you are running the 64bit version of wamp, php will refuse to load the 32 bit version of <span class="path">php_mongo.dll</span>.  While wamp and the php driver are 32bit, it’s still recommended to install the 64bit version of mongodb server if you plan on working with collections over 2GB.</p>
<p class="hr">
<h3>Links</h3>
<ul>
<li>MongoDB Downloads &#8211; <a href="http://www.mongodb.org/downloads" title="http://www.mongodb.org/downloads">http://www.mongodb.org/downloads</a></li>
<li>Win32 Mongo PHP Drivers &#8211; <a href="https://github.com/mongodb/mongo-php-driver/downloads" title="https://github.com/mongodb/mongo-php-driver/downloads">https://github.com/mongodb/mongo-php-driver/downloads</a></li>
<li>PHP MongDB Documentation &#8211; <a href="http://php.net/manual/en/book.mongo.php" title="http://php.net/manual/en/book.mongo.php">http://php.net/manual/en/book.mongo.php</a></li>
<li>10gen &#8211; <a href="http://www.10gen.com/" title="http://www.10gen.com/">http://www.10gen.com/</a></li>
<li>WampServer &#8211; <a href="http://www.wampserver.com/" title="http://www.wampserver.com/">http://www.wampserver.com/</a></li>
</ul>
<h4>Keywords:</h4><ul><li>wampserver mongodb</li><li>wamp mongodb</li><li>mongodb wamp</li><li>wamp server 2 0 mongodb</li><li>mongo db and php on wamp</li><li>wamp mongo</li><li>mongodb cakephp 2</li><li>wamp with mongodb</li><li>mongodb windows nosql getting started download windows service</li><li>mongo db 64 bit dll</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/installing-mongodb-on-windows-the-wamp-way"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/installing-mongodb-on-windows-the-wamp-way"><meta itemprop="datePublished" content="2012-02-09T12:12:12+00:00"><meta itemprop="dateModified" content="2012-02-26T09:27:02+00:00"><meta itemprop="dateCreated" content="2012-02-09T11:39:47+00:00"><meta itemprop="keywords" content="mongo,mongodb,wamp,windows"><meta itemprop="wordCount" content="684"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/installing-mongodb-on-windows-the-wamp-way/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Howto recover deleted php files using scalpel</title>
		<link>http://www.pronique.com/blog/howto-recover-deleted-php-files-using-scalpel?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=howto-recover-deleted-php-files-using-scalpel</link>
		<comments>http://www.pronique.com/blog/howto-recover-deleted-php-files-using-scalpel#comments</comments>
		<pubDate>Mon, 06 Feb 2012 02:54:48 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[file recovery]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[scalpel]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1577</guid>
		<description><![CDATA[Several times in the past I&#8217;ve accidentally overwritten a file or used a greedy wildcard with rm -rf. If you<a href="http://www.pronique.com/blog/howto-recover-deleted-php-files-using-scalpel" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><style>
span.path {
padding:0 5px;
font-family: Courier New;
background:#e8e8e8;
}
</style>
<p>Several times in the past I&#8217;ve accidentally overwritten a file or used a greedy wildcard with rm -rf. If you find yourself in this situation, don&#8217;t worry, <strong>scalpel</strong> can recover your file(s). My experience using <strong>scalpel</strong> was on a Ubuntu server but the tool will work on other linux distros including Redhat, Fedora, Debian, and more.  A Windows and Mac OS X build is also available.</p>
<p><strong>scalpel</strong> is my tool of choice because it will recover files from the newer ext4 filesystem, which is the default filesystem for Ubuntu since<span id="more-1577"></span> 9.10.</p>
<h3>Installing scalpel</h3>
<pre class="brush: plain">
//Ubuntu or Debian linux
sudo apt-get install scalpel

//Redhat or Fedora linux
sudo yum install scalpel
</pre>
<p>Once installed, you will need to edit <span class="path">/etc/scalpel/scalpel.conf</span> and uncomment the file type definition of the deleted file you are trying to recover.  Since I was trying to recover plain text php files I needed to add my own file type definition.</p>
<pre class="brush: plain">
php n   50000   &lt;?php           ?&gt;
</pre>
<p><strong>scalpel</strong> will need somewhere to copy the recovered files,  in my case I create the directory <span class="path">/tmp/scalp</span> before executing.  Be patient as the tool scans the volume extracting the found files.  scalpel will find not only deleted files but numerous old versions created each time a file is overwritten.</p>
<pre class="brush: plain">
mkdir /tmp/scalp
scalpel "/dev/sda1" -o "/tmp/scalp"
</pre>
<p>There is still a bit of work to find the most recent version of your deleted file.  scalpel will find many revisions of the file along many other php files, the trick is to find a fresh copy of your file to recover.  To accomplish this, I used grep to search for a line of code I remember adding shortly before deleting the file.</p>
<pre class="brush: plain">
grep -R “some recently added code” /tmp/scalp/*
</pre>
<p>You may end up looking through several files before finding a recent copy you are happy with.  Another thing to consider, <strong>scalp</strong> might recover a large number of files so make sure you have plenty of space on the volume you extract to.</p>
<p>Did this work you? or do you use another filesystem recover tool?  Lets here about it by leaving a comment below.</p>
<h4>Keywords:</h4><ul><li>scalpel php</li><li>scalpel recover php files</li><li>scalpel php files</li><li>red hat scalpel</li><li>linux scalpel recover php</li><li>scalpel recover php</li><li>scalpel php file</li><li>linux scalpel</li><li>how to use scalpel in ubuntu</li><li>rescue deleted php files from ubuntu</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/howto-recover-deleted-php-files-using-scalpel"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/howto-recover-deleted-php-files-using-scalpel"><meta itemprop="datePublished" content="2012-02-05T20:54:48+00:00"><meta itemprop="dateModified" content="2012-02-20T22:57:27+00:00"><meta itemprop="dateCreated" content="2012-02-05T20:26:54+00:00"><meta itemprop="keywords" content="file recovery,php,scalpel,ubuntu"><meta itemprop="wordCount" content="395"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/howto-recover-deleted-php-files-using-scalpel/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mediawiki CSV Import ver 2.2.1 Released (Coupon Codes!)</title>
		<link>http://www.pronique.com/blog/mediawiki-csv-import-ver-2-2-1-released?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mediawiki-csv-import-ver-2-2-1-released</link>
		<comments>http://www.pronique.com/blog/mediawiki-csv-import-ver-2-2-1-released#comments</comments>
		<pubDate>Fri, 04 Nov 2011 19:39:34 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1569</guid>
		<description><![CDATA[PRONIQUE Software is proud to announce a new version Mediawiki CSV Import. Version 2.2.1 introduces the Dataset Library, a collection<a href="http://www.pronique.com/blog/mediawiki-csv-import-ver-2-2-1-released" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p>PRONIQUE Software is proud to announce a new version Mediawiki CSV Import. Version 2.2.1 introduces the Dataset Library, a collection of contributed datasets that you can import and use to build your own Mediawiki Import files.</p>
<p>You must be a registered user to import or download the datasets.  Membership is only $7.99 (one time) and helps support the development of the tool.  It&#8217;s still FREE to register and use the tool however exports are limited to 15 pages until you upgrade. </p>
<p>To mark the new release and spark interest we are giving away 25 free accounts!  Use one of the coupon codes below to upgrade to a Paid account.  Once you create  an account, login and enter the coupon code on the Upgrade page.</p>
<p>SD3YD7<br />
FD3GD3<br />
MA1JT9<br />
TA9DT5<br />
TE4W2D<br />
SF7XK4<br />
SD8GW6<br />
BN2DF4<br />
OK5RT3<br />
EP8GF4<br />
ED9GR3<br />
SW2FR3<br />
FRIDAY9</p>
<p>Learn more and signup for a FREE account at <a href="http://mwcsvimport.pronique.com/">http://mwcsvimport.pronique.com/</a></p>
<p></p>
<h4>Keywords:</h4><ul><li>accounts free software with csv import</li><li>coupon codes csv</li><li>coupon csv</li><li>import csv to mongodb windows version</li><li>mediawiki bookmark</li><li>mediawiki bulk import</li></ul></span></span><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/mediawiki-csv-import-ver-2-2-1-released"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/mediawiki-csv-import-ver-2-2-1-released"><meta itemprop="datePublished" content="2011-11-04T13:39:34+00:00"><meta itemprop="dateModified" content="2011-11-04T13:39:52+00:00"><meta itemprop="dateCreated" content="2011-11-04T13:31:04+00:00"><meta itemprop="wordCount" content="178"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/mediawiki-csv-import-ver-2-2-1-released/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CakePHP: Reorder lft rght columns in Tree models</title>
		<link>http://www.pronique.com/blog/cakephp-reorder-lft-rght-columns-in-tree-models?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cakephp-reorder-lft-rght-columns-in-tree-models</link>
		<comments>http://www.pronique.com/blog/cakephp-reorder-lft-rght-columns-in-tree-models#comments</comments>
		<pubDate>Sat, 10 Sep 2011 17:57:02 +0000</pubDate>
		<dc:creator>webmaster</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Acls]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[TreeBehavior]]></category>

		<guid isPermaLink="false">http://www.pronique.com/?p=1534</guid>
		<description><![CDATA[If you are using the Tree Behavior sometimes the lft and rght columns get out of sync, especially if you<a href="http://www.pronique.com/blog/cakephp-reorder-lft-rght-columns-in-tree-models" class="searchmore">Read the Rest...</a><div class="clr"></div>]]></description>
			<content:encoded><![CDATA[<span itemprop="mainContentOfPage"><span itemprop="articleBody"><p><a href="http://www.pronique.com/wp-content/uploads/2011/09/cakephp_logo_125_trans.png"><img itemprop="image" class="alignright size-full wp-image-1721" title="CakePHP Reorder TreeBehavior" src="http://www.pronique.com/wp-content/uploads/2011/09/cakephp_logo_125_trans.png" alt="CakePHP Logo" width="125" height="125" /></a>If you are using the Tree Behavior sometimes the lft and rght columns get out of sync, especially if you are adding and deleting records during development.  Add the following method to your app/app_controller.php or controller of your choice.</p>
<pre class="brush: php">    /**
    * This is a maint function to recover/reorder the
    * lft/rght columns of a tree model.
    *
    */
    public function recover_tree( ) {
        $modelClass = $this-&gt;modelClass;
        if ( property_exists( $this-&gt;$modelClass-&gt;Behaviors, 'Tree' ) ) {
            if ( !$this-&gt;$modelClass-&gt;recover() ) {
                echo 'Error recovering acl tree';
            } else {
                echo $modelClass . ' reordered.';
            }
        } else {
            echo $modelClass . ' is not a tree model';
        }
        exit;
    }</pre>
<p>CakePHP&#8217;s Acls implementation also uses this same Tree table structure. Since Acos and Aros often don&#8217;t have their own controllers the following method will allow you to reorder the lft rght columns in one shot. Again, add this to your AppController or controller of choice.</p>
<pre class="brush: php">    /**
    * This is a maint function to recover/reorder the
    *  lft/rght columns of the aco and aro models.
    *
    */
    public function recover_acl_tree( ) {
        App::Import('Model', 'Aco' );
        App::Import('Model', 'Aro' );
        $Aco = new Aco;
        $Aro = new Aro;
        if ( !$Aco-&gt;recover() || !$Aro-&gt;recover() ) {
            echo 'Error recovering acl tree';
        } else {
            echo 'Aros and Acos reordered.';
        }
        exit;
    }</pre>
<p>These methods/actions are meant to be called manually as you feel needed. If you would rather call them from other controller actions I would suggest prepending &#8216;_&#8217; to the method names making them private/protected methods rather than url accessible actions.</p>
<p>cakephp tree reorder</p>
<h4>Keywords:</h4><ul><li>cakephp repair aro tree</li><li>cakephp tree reorder</li><li>cakephp reorder tree</li><li>recover aros cakephp</li><li>cakephp tree model</li><li>tree related function in cake php</li><li>cakephp_tree_behavior</li><li>how to use reorder data tree behaviour in cakephp</li><li>model keywords in cake php</li><li>rght</li></ul></span></span><meta itemprop="isFamilyFriendly" content="Y"><div class="schema_property_wrap"></div><meta itemprop="url" content="http://www.pronique.com/blog/cakephp-reorder-lft-rght-columns-in-tree-models"><meta itemprop="discussionUrl" content="http://www.pronique.com/blog/cakephp-reorder-lft-rght-columns-in-tree-models"><meta itemprop="datePublished" content="2011-09-10T11:57:02+00:00"><meta itemprop="dateModified" content="2012-02-13T08:21:17+00:00"><meta itemprop="dateCreated" content="2011-09-10T11:55:44+00:00"><meta itemprop="keywords" content="Acls,CakePHP,TreeBehavior"><meta itemprop="wordCount" content="282"><meta itemprop="blogPosts" content="http://www.pronique.com">]]></content:encoded>
			<wfw:commentRss>http://www.pronique.com/blog/cakephp-reorder-lft-rght-columns-in-tree-models/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using apc
Object Caching 1547/1724 objects using apc

Served from: www.pronique.com @ 2012-05-20 18:20:07 -->
