How to use Interface to overcome multiple if, else - php

NOTE: I have added only snippets of my code.
I have to validate Partner user while they register and if the validation passes then we create a plan for them else we treat them as a normal user and won't create a plan for them.
public function givePlan(Request $request){
/* Validation Code Goes Here */
try{
$data = $request->all();
/*Get partner details*/
$partner = $this->getPartnerDetailsByUniqueId($data['partner_id']);
/** Validate the user details */
$this->validateUser($partner, $data['phone']);
/* Rest of the code */
}catch(\Exception $e){
/* 400 handle the exception */
}
}
protected function validateUser($partner, $phone){
$partnerObj = new ValidatePartner();
if($partner == 'partner1'){
$partnerObj->validatePartner1($phone);
/* rest of the code */
}else if($partner == 'partner2'){
$partnerObj->validatePartner2($phone);
/* rest of the code */
}else if($partner == 'partner3'){
$partnerObj->validatePartner3($phone);
/* rest of the code */
}
}
The problem over here is we need to check the partner with multiple if else condition and then validate the user.
I know we can use the Polymorphism with this and can be achieved by Interface as follows
interface ValidateUser {
public function validate();
}
class Partner1 implements ValidateUser{
public function validate(){
}
}
But again I need to check and create the partner object and the following code still exists
$partnerObj = new Partner1();
$partnerObj->validate($phone);
I had read somewhere that we can pass directly the interface object to achieve this without the object knowledge. Can anyone help me out with this issue?
Any other programming language implementation with Interface or similar feature of PHP is welcome.

Implementing interface having simple function validate.
class partnerFactory {
public static function getType($type) {
switch ($type) {
case 'partner1':
return new Partner1();
case 'partner2':
return new Partner1();
default:
break;
}
}
}
class Partner1 implements ValidateUser {
public function validate() {
// your function here.
}
}
interface ValidateUser {
public function validate() { }
}
$partner1 = partnerFactory::getType('partner1');
$partner1->validate();
$partner2 = partnerFactory::getType('partner2');
$partner2->validate();

Related

"Early return" best practice and DRY

I'm wondering is there a better way of "braking out" of method under some conditions. Let me better explain this with code:
function execute($context)
{
// some init actions
$event = new BeforeOperationOne();
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return;
}
$result = $this->operationOne();
// some other code
$event = new BeforeOperationTwo();
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return;
}
// this is not important what is access checker,
// this is just to show that all following code uses data
// computed in previous steps
$accessChecker = new AccessChecker($result);
$this->operationTwo(accessChecker);
// some other code
$event = new BeforeOperationThree();
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return;
}
$this->operationThree();
// some other code
}
We have repeated here the condition, setting user in context when user has access from event. What options I can think about is:
The ugly do-while(false) or goto (I better leave it this way as it is now)
Extract this to method and change the condition to if (!$this->handleEvent($event, $context)) { return; }- This doesn't help to much and cannot think a better name handle doesn't say it's returning something
Build array of closures for operations and loop it through checking. We can assume that all event classes are derived from common class with accessGranted methods. This can be ugly as some operations need data from previous "steps", I would have to keep them outside and pass them.
Throw and catch exceptions that user has access - another bad solution.
Do you have any ideas how to write it better?
#Greg i was thinking about something like that:
abstract class Handler
{
protected $nextHandler = null;
abstract public function Request($request);
public function setNextHandler(Handler $handler)
{
$this->nextHandler = $handler;
}
protected function someOperations($event)
{
//i copied this section, so you must shape that
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return true;
}
return false;
}
}
class BeforeOperationOneHandler extends Handler
{
public function Request($request)
{
if ($this->someOperations(new BeforeOperationOne())) {
return;
}
$result = $this->operationOne(); // shape this too
return $this->nextHandler->Request($result);
}
}
class BeforeOperationTwoHandler extends Handler
{
public function Request($request)
{
if ($this->someOperations(new BeforeOperationTwo())) {
return;
}
$accessChecker = new AccessChecker($result); // shape this too
$result = $this->operationTwo(accessChecker);
return $this->nextHandler->Request($result);
}
}
class BeforeOperationThreeHandler extends Handler
{
public function Request($request)
{
if ($this->someOperations(new BeforeOperationThree())) {
return;
}
$result = $this->operationThree(); // shape this too
return $this->nextHandler->Request($result);
}
}
class DefaultHandler extends Handler
{
public function Request($request)
{
// this is the last step
}
}
function execute($context)
{
// some init actions
$beforeOperationOneHandler = new BeforeOperationOneHandler();
$beforeOperationTwoHandler = new BeforeOperationTwoHandler();
$beforeOperationThreeHandler = new BeforeOperationThreeHandler();
$defaultHandler = new DefaultHandler();
// set the sequence of the elements
// BeforeOperationOneHandler > BeforeOperationTwoHandler > BeforeOperationThreeHandler> DefaultHandler
$beforeOperationOneHandler->setNextHandler($beforeOperationTwoHandler);
$beforeOperationTwoHandler->setNextHandler($beforeOperationThreeHandler);
$beforeOperationThreeHandler->setNextHandler($defaultHandler);
return $beforeOperationOneHandler->Request($some_init);
}
It's only quickly written shape of "chain of responsibility" pattern so i thoughtlessly copied some of your code fragments
I hope this will lead you to a better solution

Switching Classes during __construct()

Let's take the following classes for a moment:
class member {
public $id;
public function __construct($id) {
$this->id = $id;
// Check user level
if ($this->check_user_level() == moderator) {
$this = new moderator($this->id);
}
}
private function check_user_level() {
// Return user level from system based on user ID
}
}
class moderator extends member {
public function show_moderator_tools() {
// etc.
}
}
==================
$user = new member($user_id);
The desired behavior is to have $user detect whether the user is authorized to have moderator access, and if so recast the user using the moderator class instead of member.
PHP forbids simply reassigning $this, so what appears to be the best solution would be one of the following:
Run check_user_level($id) as a regular function and using an if statement
$user_level = check_user_level($id);
if ($user_level == "moderator") {
$user = new moderator($id);
} else {
$user = new member($id);
}
Set a flag in the base class that can be used for a check/redefinition after initializing $user
$user = new member($id);
if ($user->flag = TRUE) {
$user = new moderator($id);
}
The latter feels like it's introducing a security flaw, especially since the flag (which could just as easily be $user->user_level, or similar, I guess) would have to be public to be able to check it afterward.
What I would like to do would be to just make one call to new member($id) and have it handle things automatically from there, without the need for if statements after the fact. Is there a way to do this?
You can of do this by introducing another class (lets call it user) and using the __call magic method in php and call_user_func_array for calling the methods.
The logic is something like this -
Create a user class that has no method except check_user_level. It checks proper details and assigns it's $obj to the instance of either member or moderator class.
Here is how the classes would look like (I've changed the functions to print something out)-
class user{
public function __construct($id) {
$this->id = $id;
if ($this->check_user_level() == "moderator") {
$this->obj = new moderator($this->id);
}else{
$this->obj = new member($this->id);
}
}
public function __call($method, $args){
call_user_func_array(array($this->obj,$method), $args);
}
public function __get($prop){
if(isset($this->obj->$prop)){
return $this->obj->$prop;
}
return NULL;
}
private function check_user_level() {
// Return user level from system based on user ID
if($this->id == 1){
return "moderator";
}
return "member";
}
}
class member {
public $id;
public function __construct($id) {
$this->id = $id;
}
public function show_message($arg){
var_dump($this->id.$arg);
}
}
class moderator extends member{
public function show_moderator_tools() {
var_dump($this->id ."My toolset!");
}
}
So, now you can simply call the user class and that will automatically decide if it's a member or a moderator and call the method if it exists.
//Trying out a few examples
//Creating a member user
$mem_obj = new user(213);
$mem_obj->show_message("New message");
//Creating a moderator user
$mod_obj = new user(1);
$mod_obj->show_moderator_tools();
/*
OUTPUTS
-------
string(14) "213New message"
string(12) "1My toolset!"
*/
But you need to be careful with these kind of hacks.
For instance -
//This will fail
//because mem_obj doesn't have show_moderator_tools()
$mem_obj->show_moderator_tools();
EDIT
You can similarly go ahead with redirecting to properties using __get.
I have modified the code above to add this method beneath __call.
//Testing it
var_dump($mem_obj->id);
//This is an illegal property
var_dump($mem_obj->illegelProperty);
/*
OUTPUTS
int(213)
NULL
*/

Cleaning up my Application Structure... Domain Objects and Data Mappers

I've been developing an application utilizing ZendFramework 1.1 for the better part of two years now, and as-so it has seen a few different stages of refactoring from me learning or trying something new. At its current state, I feel that my structure is pretty good in that I can get stuff done quickly, but could certainly use some improvements in certain areas- where I feel like there is a lot of bloat and awkward dependencies.
Bear with me here as I lay down some example code from my application. I will use an example of an Order object which has OrderItem instances which also must be saved. I will explain all necessary parts of instantiation and saving.
As far as my understanding goes, what I've got going on here is more-so in line with the ActiveRecord design pattern than with Domain Models, though I think I have practices from both...
class Order extends BaseObject {
/** #var OrderItem array of items on the order */
public $items = array();
public function __construct($data = array()){
// Define the attributes for this model
$schema = array(
"id" => "int", // primary key
"order_number" => "string", // user defined
"order_total" => "float", // computed
// etc...
);
// Get datamapper and validator classes
$mf = MapperFactory::getInstance();
$mapper = $mf->get("Order");
$validator = new Order_Validator();
$table = new Application_DbTable_Order();
// Construct parent
parent::__construct($schema, $mapper, $validator, $table);
// If data was provided then parse it
if(count($data)){
$this->parseData($data);
}
// return the instance
return $this;
}
// Runs before a new instance is saved, does some checks
public function addPrehook(){
$orderNumber = $this->getOrderNumber();
if($this->mapper->lookupByOrderNumber($orderNumber)){
// This order number already exists!
$this->addError("An order with the number $orderNumber already exists!");
return false;
}
// all good!
return true;
}
// Runs after the primary data is saved, saves any other associated objects e.g., items
public function addPosthook(){
// save order items
if($this->commitItems() === false){
return false;
}
// all good!
return true;
}
// saves items on the order
private function commitItems($editing = false){
if($editing === true){
// delete any items that have been removed from the order
$existingOrder = Order::getById($this->getId());
$this->deleteRemovedItems($existingOrder);
}
// Iterate over items
foreach($this->items as $idx => $orderItem){
// Ensure the item's order_id is set!
$orderItem->setOrderId($this->getId());
// save the order item
$saved = $orderItem->save();
if($saved === false){
// add errors from the order item to this instance
$this->addError($orderItem->getErrors());
// return false
return false;
}
// update the order item on this instance
$this->items[$idx] = $saved;
}
// done saving items!
return true;
}
/** #return Order|boolean The order matching provided ID or FALSE if not found */
public static function getById($id){
// Get the Order Datamapper
$mf = MapperFactory::getInstance();
$mapper = $mf->get("Order");
// Look for the primary key in the order table
if($mapper->lookup($id)){
return new self($mapper->fetchObjectData($id)->toArray());
}else{
// no order exists with this id
return false;
}
}
}
The parsing of data, saving, and pretty much anything else that applies to all models (a more appropriate term may be Entity) exists in the BaseObject, as so:
class BaseObject {
/** #var array Array of parsed data */
public $data;
public $schema; // valid properties names and types
public $mapper; // datamapper instance
public $validator; // validator instance
public $table; // table gateway instance
public function __construct($schema, $mapper, $validator, $table){
// raise an error if any of the properties of this method are missing
$this->schema = $schema;
$this->mapper = $mapper;
$this->validator = $validator;
$this->table = $table;
}
// parses and validates $data to the instance
public function parseData($data){
foreach($data as $key => $value){
// If this property isn't in schema then skip it
if(!array_key_exists($key, $this->schema)){
continue;
}
// Get the data type of this
switch($this->schema[$key]){
case "int": $setValue = (int)$value; break;
case "string": $setValue = (string)$value; break;
// etc...
default: throw new InvalidException("Invalid data type provided ...");
}
// Does our validator have a handler for this property?
if($this->validator->hasProperty($key) && !$this->validator->isValid($key, $setValue)){
$this->addError($this->validator->getErrors());
return false;
}
// Finally, set property on model
$this->data[$key] = $setValue;
}
}
/**
* Save the instance - Inserts or Updates based on presence of ID
* #return BaseObject|boolean The saved object or FALSE if save fails
*/
public function save(){
// Are we editing an existing instance, or adding a new one?
$action = ($this->getId()) ? "edit" : "add";
$prehook = $action . "Prehook";
$posthook = $action . "Posthook";
// Execute prehook if its there
if(is_callable(array($this, $prehook), true) && $this->$prehook() === FALSE){
// some failure occured and errors are already on the object
return false;
}
// do the actual save
try{
// mapper returns a saved instance with ID if creating
$saved = $this->mapper->save($this);
}catch(Exception $e){
// error occured saving
$this->addError($e->getMessage());
return false;
}
// run the posthook if necessary
if(is_callable(array($this, $posthook), true) && $this->$posthook() === FALSE){
// some failure occured and errors are already on the object
return false;
}
// Save complete!
return $saved;
}
}
The base DataMapper class has very simple implementations for save, insert and update, which are never overloaded because of the $schema being defined per-object. I feel like this is a bit wonky, but it works I guess? Child classes of BaseMapper essentially just provide domain-specific finder functions e.g., lookupOrderByNumber or findUsersWithLastName and other stuff like that.
class BaseMapper {
public function save(BaseObject $obj){
if($obj->getId()){
return $this->update($obj);
}else{
return $this->insert($obj);
}
}
private function insert(BaseObject $obj){
// Get the table where the object should be saved
$table = $obj->getTable();
// Get data to save
$saveData = $obj->getData();
// Do the insert
$table->insert($saveData);
// Set the object's ID
$obj->setId($table->getAdapter()->getLastInsertId());
// Return the object
return $obj;
}
}
I feel like what I have isn't necessarily horrible, but I also feel like there are some not-so-great designs in place here. My concerns are primarily:
Models have a very rigid structure which is tightly coupled to the database table schema, making adding/removing properties from the model or database table a total pain in the butt! I feel like giving all of my objects which save to the database a $table and $mapper in the constructor is a bad idea... How can I avoid this? What can I do to avoid defining $schema?
Validation seems a bit quirky as it is tied very tightly to the property names on the model which also correspond to column names in the database. This further complicates making any database or model changes! Is there a more appropriate place for validation?
DataMappers don't really do much besides provide some complicated finder functions. Saving complex objects is handled entirely by the object class itself (e.g., Order class in my example. Also is there an appropriate term for this type of object, other than 'complex object'? I say that my Order object is "complex" because it has OrderItem objects that it must also save. Should a DataMapper handle the saving logic that currently exists in the Order class?
Many thanks for your time and input!
It's a good practice to separate the concerns between objects as much as possible. Have one responsible for Input Validation, other to perform the business logic, DB operations, etc. In order to keep the 2 objects loosely coupled they should not know anything about each other’s implementation only what they can do. This is defined thru an interface.
I recommend reading this article http://www.javaworld.com/article/2072302/core-java/more-on-getters-and-setters.html and other ones from this guy. He's got a book as well worth reading http://www.amazon.com/Holub-Patterns-Learning-Looking-Professionals/dp/159059388X.
I would separate if possible order and items, I don’t know much about your app but if you need to show a list of 20 orders only with their order numbers then those DB calls and processing regarding order items would be a waste if not separated. This is of course not the only way.
So first you need to know what the order attributes are and encapsulate a way to feed those into an order and also have an order expose that data to other objects.
interface OrderImporter {
public function getId();
public function getOrderNumber();
public function getTotal();
}
interface OrderExporter {
public function setData($id, $number, $total);
}
In order to keep the business logic separate from the database we need to encapsulate that behavior as well like so
interface Mapper {
public function insert();
public function update();
public function delete();
}
Also I would define a specific mapper whose duty is to handle DB operations regarding orders.
interface OrderMapper extends Mapper {
/**
* Returns an object that captures data from an order
* #return OrderExporter
*/
public function getExporter();
/**
* #param string $id
* #return OrderImporter
*/
public function findById($id);
}
Finally an order needs to be able to communicate with all those objects through some messages.
interface Order {
public function __construct(OrderImporter $importer);
public function export(OrderExporter $exporter);
public function save(OrderMapper $orderRow);
}
So far we have a way to provide data to the Order, a way to extract data from the order and a way to interact with the db.
Below I've provided a pretty simple example implementation which is far from perfect.
class OrderController extends Zend_Controller_Action {
public function addAction() {
$requestData = $this->getRequest()->getParams();
$orderForm = new OrderForm();
if ($orderForm->isValid($requestData)) {
$orderForm->populate($requestData);
$order = new ConcreteOrder($orderForm);
$mapper = new ZendOrderMapper(new Zend_Db_Table(array('name' => 'order')));
$order->save($mapper);
}
}
public function readAction() {
//if we need to read an order by id
$mapper = new ZendOrderMapper(new Zend_Db_Table(array('name' => 'order')));
$order = new ConcreteOrder($mapper->findById($this->getRequest()->getParam('orderId')));
}
}
/**
* Order form can be used to perform validation and as a data provider
*/
class OrderForm extends Zend_Form implements OrderImporter {
public function init() {
//TODO setup order input validators
}
public function getId() {
return $this->getElement('orderID')->getValue();
}
public function getOrderNumber() {
return $this->getElement('orderNo')->getValue();
}
public function getTotal() {
return $this->getElement('orderTotal')->getValue();
}
}
/**
* This mapper also serves as an importer and an exporter
* but clients don't know that :)
*/
class ZendOrderMapper implements OrderMapper, OrderImporter, OrderExporter {
/**
* #var Zend_Db_Table_Abstract
*/
private $table;
private $data;
public function __construct(Zend_Db_Table_Abstract $table) {
$this->table = $table;
}
public function setData($id, $number, $total) {
$this->data['idColumn'] = $id;
$this->data['numberColumn'] = $number;
$this->data['total'] = $total;
}
public function delete() {
return $this->table->delete(array('id' => $this->data['id']));
}
public function insert() {
return $this->table->insert($this->data);
}
public function update() {
return $this->table->update($this->data, array('id' => $this->data['id']));
}
public function findById($id) {
$this->data = $this->table->fetchRow(array('id' => $id));
return $this;
}
public function getId() {
return $this->data['idColumn'];
}
public function getOrderNumber() {
return $this->data['numberColumn'];
}
public function getTotal() {
return $this->data['total'];
}
public function getExporter() {
return $this;
}
}
class ConcreteOrder implements Order {
private $id;
private $number;
private $total;
public function __construct(OrderImporter $importer) {
//initialize this object
$this->id = $importer->getId();
$this->number = $importer->getOrderNumber();
$this->total = $importer->getTotal();
}
public function export(\OrderExporter $exporter) {
$exporter->setData($this->id, $this->number, $this->total);
}
public function save(\OrderMapper $mapper) {
$this->export($mapper->getExporter());
if ($this->id === null) {
$this->id = $mapper->insert();
} else {
$mapper->update();
}
}
}

Validation Class with fluent interface

I'm currently struggling with some form validation. I'm working with the class below, which is intended to be a fluent interface.
class Validator implements ValidatorInterface {
protected $_count_validators = 0;
protected $_validators;
protected $errorMsg;
public function __construct($errorMsg = '')
{
$this->errorMsg = $errorMsg;
}
public function addValidator(ValidatorInterface $validator)
{
$this->_count_validators++;
$this->_validators[] = $validator;
return $this;
}
public function validate($value)
{
foreach($this->_validators as $validator) {
if ($validator->validate($value) === false) {
return false;
}
}
return true;
}
public function getError()
{
return $this->errorMsg;
}
}
It actually works 75 % - and I can add validators like this:
$postalcodeValidator = new \Framework\Formular\Validator\Validator();
$validatePostalcode= $postalcodeValidator->addValidator(new \Framework\Formular\Validator\NotEmpty)
->addValidator(new \Framework\Formular\IsNumeric);
$cityValidator = new \Framework\Formular\Validator\Validator();
$validateCity = $lastnameValidator->addValidator(new \Framework\Formular\Validator\NotEmpty);
Now I can just write:
$result = $postalcodeValidator->validate('00000');
- or -
$result = $cityValidator->validate('London');
And I will have a boolean.
My problem is, that I need to make it easy to set some errors. In the above example - if I just added a getErrors()-function in the class - I had to get the errors for every new instantiation of the class. I want to make a function for getting all errors.
Can you help me on a solution for that?
Thanks in advance,
denlau
A simple way is to implement a static member in an abstract validator class. All your concrete Validator extends this, and will add automaticly errors to this member. Finally you can get this static member with one call. But this is an anti-pattern and you have to reset this member after getting errors.
A better practice is to use the composite pattern. A class where you can add one or more elements with assigned validators. This composite class will execute all validators on your assigned elements and collect all error messages from each validator. Then you can retrieve all collected error messages from your composite, with one call.
For more information about composite pattern see here on wiki
Here an example..
$elementA = new ElementA; // implements Validable
$elementA->addValidator(new ValidatorA)->addValidator(new ValidatorB);
$elementB = new ElementB; // implements Validable
$elementB->addValidator(new ValidatorC);
$elementA->setValue('any_posted_value_to_validate');
$elementB->setValue('another_any_posted_value_to_validate');
$composite = new Composite; // implements Validable
$composite->addElement($elementA)->addElement($elementB);
if (!composite->isValid()) { // will execute all validators on all elements
$errorMessages = $composite->getErrors();
}
Within your composite..
public function isValid()
{
$isValid = true;
foreach ($this->elements as $element) {
if (!$element->isValid()) { // will execute all assigned validators to this element
$this->addErrors($element->getErrors());
$isValid = false;
}
}
return $isValid;
}
The Validable Interface
interface Validable
{
public function isValid();
public function getErrors();
}

Using Interfaces in Kohana 3.1.3

I'm trying to build a form wizard in Kohana and am learning a bit as I go. One of the things that I've learn might work best is utilizing a state pattern in my class structure to manage the different steps a user can be in during the form process.
After doing some research, I've been thinking that the best approach may be to use an interface and have all of the steps act as states that implement the interface. After a state validates, it will change a session variable to the next step, which can be read upon the initial load of the interface and call the correct state to use.
Does this approach make sense? If so, how the heck do I make it happen (how do I best structure the filesystem?)
Here is the rough start I've been working on:
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Project_Builder #state
* Step_One #state
* Step_Two #state
**/
interface Project_Builder
{
public function do_this_first();
public function validate();
public function do_this_after();
}
class Step_One implements Project_Builder {
public function __construct
{
parent::__construct();
// Do validation and set a partial variable if valid
}
public function do_this_first()
{
echo 'First thing done';
// This should be used to set the session step variable, validate and add project data, and return the new view body.
$session->set('step', '2');
}
public function do_this_after()
{
throw new LogicException('Have to do the other thing first!');
}
}
class Step_Two implements Project_Builder {
public function do_this_first()
{
throw new LogicException('Already did this first!');
}
public function do_this_after()
{
echo 'Did this after the first!';
return $this;
}
}
class Project implements Project_Builder {
protected $state;
protected $user_step;
protected $project_data
public function __construct()
{
// Check the SESSION for a "step" entry. If it does not find one, it creates it, and sets it to "1".
$session = Session::instance('database');
if ( ! $session->get('step'))
{
$session->set('step', '1');
}
// Get the step that was requested by the client.
$this->user_step = $this->request->param('param1');
// Validate that the step is authorized by the session.
if ($session->get('step') !== $this->user_step)
{
throw new HTTP_Exception_404('You cannot skip a step!');
}
// Check if there is user data posted, and if so, clean it.
if (HTTP_Request::POST == $this->request->method())
{
foreach ($this->request->post() as $name => $value)
{
$this->project_data["$name"] = HTML::chars($value);
}
}
// Trigger the proper state to use based on the authorized session step (should I do this?)
$this->state = new Step_One;
}
public function doThisFirst()
{
$this->state = $this->state->do_this_first();
}
public function doThisAfter()
{
$this->state = $this->state->do_this_after();
}
}
$project = new Project;
try
{
$project->do_this_after(); //throws exception
}
catch(LogicException $e)
{
echo $e->getMessage();
}
$project = new Project;
$project->do_this_first();
$project->validate();
$project->do_this_after();
//$project->update();
Your way certainly looks possible, however I would be tempted to keep it simpler and use some of Kohanas build in features to take care of what you want. For example, I would use Kostache (mustache) and have separate View classes (and potentially templates) for each step. Then the controller becomes quite simple. See the example below (missing session stuff and validation of the step_number). All of the validation is handled in the model. If there is a validation error, an exception can be thrown which can then pass error messages back to the View.
<?php
class Wizard_Controller {
function action_step($step_number = 1)
{
$view = new View_Step('step_' + $step_number);
if ($_POST)
{
try
{
$model = new Model_Steps;
$model->step_number = $step_number;
if ($model->save($_POST))
{
// Go to the next step
$step_number++;
Request::current()->redirect('wizard/step/'.$step_number);
}
}
catch (Some_Kind_Of_Exception $e)
{
$view->post = $_POST;
$view->errors = $e->errors();
}
}
$view->render();
}
}
?>
Hope this makes sense.

Categories