PHPackages                             webtechnick/draft - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. webtechnick/draft

ActiveCakephp-plugin[Framework](/categories/framework)

webtechnick/draft
=================

CakePHP Draft Plugin

651[1 issues](https://github.com/webtechnick/CakePHP-Draft-Plugin/issues)PHP

Since Oct 7Pushed 10y ago1 watchersCompare

[ Source](https://github.com/webtechnick/CakePHP-Draft-Plugin)[ Packagist](https://packagist.org/packages/webtechnick/draft)[ RSS](/packages/webtechnick-draft/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

Draft CakePHP Plugin
====================

[](#draft-cakephp-plugin)

- Author: Nick Baker
- Version: 1.0
- License: MIT
- Website:

Features
--------

[](#features)

CakePHP Plugin to Automatically save drafts of any model, allowing for data recovery of progress made persisting through authentication timeouts or power outages.

Requirements
------------

[](#requirements)

CakePHP 2.x jQuery (if you want to use the auto draft and save draft buttons in the helper)

Changelog
---------

[](#changelog)

- 1.0 Initial release
- 0.1 Start of project

Install
-------

[](#install)

Clone the repository into your `app/Plugin/Draft` directory:

```
$ git clone git://github.com/webtechnick/CakePHP-Draft-Plugin.git app/Plugin/Draft

```

Run the schema into your database:

```
$ cake schema create --plugin Draft

```

Setup
-----

[](#setup)

Load the plugin in your bootstrap.php. Update your your `app/Config/bootstrap.php` file with:

```
CakePlugin::load('Draft');

```

Description
-----------

[](#description)

Drafts are a way to have a "future" state of your current production record that mutliple people can see and edit without having to save to the actual table. The idea is your users should never lose work they've made in a form and should be able to come back to work from whereever they leave off last. Drafts also have the added benefit of persisting through authentication timeouts as well as any other form completion interuption.

You can think of Drafts as a Google Doc, where there is only one "Draft" state of any record at a time, users are tracked as who made the most recent change to the draft. Anyone with access to edit the record has access to overwrite/update the draft state.

You can also create Drafts of records that don't exist yet (new records). So you can create a draft state of new content, and it's associated with your user (AuthComponent::user('id') or $Model-&gt;getUserId() definition). You can find and continue editing any new content from draft state, clicking save will delete the draft after a successful save has been made on the Model.

There are a number of features the plugin offers as well as helpers for auto saving as draft (require's jQuery).

Usage
-----

[](#usage)

Attach the Draftable Behavior to any Model you wish to allow drafts for.

```
public $actsAs = array('Draft.Draftable');

```

### Save A Draft

[](#save-a-draft)

There are three ways in the behavior to save a draft version of your current record. All of which will NOT update the actual record, but instead create a Draft of the record for later recovery/editing.

```
$data = array(
	'Model' => array(
		'field1' => 'value',
	)
);
//saves the data as a draft
$this->Model->save($data, array('draft' => true));

$data = array(
	'Model' => array(
		'draft' => true, //magic key!
		'field1' => 'value',
	)
);
//will save as draft because 'draft' is set to true in $data['Model']
$this->Model->save($data);

$this->Model->data = array(
	'Model' => array(
		'field1' => 'value',
	)
);
//will save any data in $Model->data as draft.
$this->Model->saveDraft();

```

### Find A Draft

[](#find-a-draft)

Recover a Draft record and merge it with your actual record to continue editing (keeping the original intact)

Find the data first, using the model find, but also find the draft of this record. Then merge the data together, where the 'Model' key will be the Drafted version. A new key of `ModelOriginal` will contain the original record.

```
$data = $this->Model->find('first', array(
	'draft' => true
));
$this->request->data = $this->Model->mergeDraft($data);

```

Another option is using contain, the rest is the same.

```
$this->Model->bindDraft();
$data = $this->Model->find('first', array(
	'contain' => array('Draft')
));
$this->request->data = $this->Model->mergeDraft($data);

```

You can also use the custom findDraft method which does the merge for your automatically

```
$this->request->data = $this->Model->findDraft(array(
	'conditions' => array(
		'Model.id' => 1,
		//your other conditions
	),
	'contain' => array(
		//your contains
	)
));

```

The data return (and Merged) will include any contains you included along with the Draft. If there is an active Draft, the Draft changes are merged into the Model, and a new Key of `ModelOringial` is created for your reference and ease of switching between draft and original record. (draft being the default editable in your forms).

Model with an active draft.

```
$data = array(
	'Model' => array(
		'field' => 'value',
		'field2' => 'value',
		'is_draft' => true,
	),
	'Draft' => array(
		'id' => 'long-string-id',
		'user_id' => 1,
		//...
	),
	'ModelOriginal' => array(
		'field' => 'original_value',
		'field2 => 'original_value',
	)
);

```

Model without an active draft.

```
$data = array(
	'Model' => array(
		'field' => 'value',
		'field2' => 'value',
	),
	'Draft' => array(
		'id' => null,
		'user_id' => null,
		//...
	),
);

```

### Find Draft by User

[](#find-draft-by-user)

You can also save drafts of records that aren't actually create yet. Drafts are automatically tracked to a user, to use this feature you'll need to either be using `AuthComponent::user('id')` or you'll need to define `$Model->getUserId()`

NOTE: You can define `getUserId()` in the AppModel

```
public function getUserId() {
	//your custom method to get the user_id string or int.
}

```

Otherwise `AuthComponent::user('id');` is used instead.

Once you can track users, you can then retrieve drafts that don't have a record associated with them (new unsaved records)

```
$this->request->data = $this->Model->findDraftByUser(); //Find draft by logged in user
$this->request->data = $this->Model->findDraftByUser(1); //Find draft by user_id of 1

```

### Delete A Draft

[](#delete-a-draft)

```
$this->Model->deleteDraft(); //will delete any draft data saved to $Model->id;
$this->Model->deleteDraft(1); //will delete any draft data saved to this model with the model's id of 1;

```

Example Setups
--------------

[](#example-setups)

#### Controller Changes

[](#controller-changes)

Update your Controller to find and merge drafts for your request data.

```
$this->request->data = $this->Model->findDraft(array(
	'conditions' => array('Model.id' => $id),
));

```

Full example of edit function.

```
//Example Edit finding and restoring draft data
function edit($id = null) {
	if (!empty($this->request->data)) {
		if ($this->Model->saveAll($this->request->data)) {
			$this->Session->setFlash('Success!');
			return $this->redirect(array('action' => 'index'));
		} else {
			$this->Session->setFlash('Failures');
		}
	}
	//Altered!
	if ($id && empty($this->request->data)) {
		$this->request->data = $this->Model->findDraft(array(
			'conditions' => array('Model.id' => $id),
			'contain' => array('Association')
		));
	}
}

```

You'll want to alter your add function as well if you intend on allowing drafts of unsaved content (new records)

```
if ($data = $this->Model->findDraftByUser()) {
	$this->request->data = $data;
}

```

Full example of add function.

```
//Example Add Pulling up Draft data from unsaved records.
function add() {
	if (!empty($this->request->data)) {
		if ($this->Model->save($this->request->data)) {
			$this->Session->setFlash(__('The Model has been saved'));
			$this->redirect(array('action' => 'index'));
		} else {
			$this->Session->setFlash(__('The Model could not be saved. Please, try again.'));
		}
	}
	//Altered!
	if ($data = $this->Model->findDraftByUser()) {
		$this->request->data = $data;
	}
}

```

#### View Changes

[](#view-changes)

##### Save Draft Button

[](#save-draft-button)

You'll want to add the `Save Draft` Button. This feature requires the name of the Model you have Draftable on, as well as the HTML ID of the form you wish to Draft

If you have a form like this:

```

```

Your Draft Button would be this:

```

```

This feature uses jQuery to attach and execute the draft save to the custom drafts controller via ajax (recommended setup). If you don't want the auto script and you want to handle the draft save yourself you can turn off the auto scripting by adding `'script' => false` in the options.

```

```

Any additional options for the link, put into the options array.

```

```

##### Discard Button

[](#discard-button)

You'll also want the `Discard Draft' Button. When you're editing a draft, you'll also want the discard draft button to allow the user to blow away the draft and start fresh.

```

```

Again you can pass in any link option into the options and it will keep.

```

```

ProTip: Only show the discard button if you're actively editing a Draft. Do so by checking for the `is_draft` field in the data.

```

```

##### Auto Draft Feature

[](#auto-draft-feature)

You can set an automatic timer to save drafts at certain intervals (every 60 seconds is default). Again, like Save Draft feature -- this feature requires jQuery to work.

```

```

Basic Admin
-----------

[](#basic-admin)

You can view and search all open drafts using the built in admin system.

Navigate to `http://example.com/admin/draft/drafts` to see all your saved drafts.

#### ENJOY! Comments are appreciated

[](#enjoy-comments-are-appreciated)

###  Health Score

21

—

LowBetter than 18% of packages

Maintenance17

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/11288cf7d118c49c118ca38127380beffe290cf84a4b01243e4177e6e8fd319c?d=identicon)[webtechnick](/maintainers/webtechnick)

---

Top Contributors

[![webtechnick](https://avatars.githubusercontent.com/u/26438?v=4)](https://github.com/webtechnick "webtechnick (21 commits)")

### Embed Badge

![Health badge](/badges/webtechnick-draft/health.svg)

```
[![Health](https://phpackages.com/badges/webtechnick-draft/health.svg)](https://phpackages.com/packages/webtechnick-draft)
```

###  Alternatives

[laravel/telescope

An elegant debug assistant for the Laravel framework.

5.2k67.8M192](/packages/laravel-telescope)[spiral/roadrunner

RoadRunner: High-performance PHP application server and process manager written in Go and powered with plugins

8.4k12.2M84](/packages/spiral-roadrunner)[nolimits4web/swiper

Most modern mobile touch slider and framework with hardware accelerated transitions

41.8k177.2k1](/packages/nolimits4web-swiper)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M259](/packages/laravel-dusk)[laravel/prompts

Add beautiful and user-friendly forms to your command-line applications.

708181.8M596](/packages/laravel-prompts)[cakephp/chronos

A simple API extension for DateTime.

1.4k47.7M121](/packages/cakephp-chronos)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
