Skip to content

Instantly share code, notes, and snippets.

@asaokamei
Created December 1, 2012 21:51
Show Gist options
  • Save asaokamei/4185350 to your computer and use it in GitHub Desktop.
Save asaokamei/4185350 to your computer and use it in GitHub Desktop.
an interaction demo.
<?php
namespace Interaction;
$intAct = Core::get( 'Interaction\interact' );
$intAct->action( 'saveEntity3Steps', 'form' );
class interact extends \wsModule\Alt\Web\Interaction
{
/** @var \WScore\Html\PageView */
protected $view;
/**
* @param \wsModule\Alt\Web\Request $request
* @param \WScore\Web\Session $session
* @param \WScore\DbAccess\Role $role
* @param \Interaction\view1|\Interaction\view2 $view
* @DimInjection Get \wsModule\Alt\Web\Request
* @DimInjection Fresh Session
* @DimInjection Get \WScore\DbAccess\Role
* @DimInjection Get interactView
*/
public function __construct( $request, $session, $role, $view )
{
parent::__construct( $request, $session, $role );
$this->view = $view;
}
/**
* @return view1|view2|\WScore\Html\PageView
*/
public function getView() {
return $this->view;
}
/**
* inserts entity in 3 steps: form -> confirm -> save/done.
*
* @param string $action
*/
public function saveEntity3Steps( $action )
{
// get entity from saved in the session.
$entity = $this->restoreData( 'entity' );
if( !$entity ) {
// create new entity.
$this->clearData();
$entity = $this->role->em->newEntity( 'Interaction\model' );
$this->registerData( 'entity', $entity );
}
// show the input form. also load and validates the input.
if( $this->contextFormAndLoad( $entity, $action, 'form' ) ) {
$this->view->set( 'action', 'form' );
$this->view->showForm_form( $entity, 'form' );
return;
}
// show the confirm view. save token as well.
if( $this->contextValidateUnless( $entity, $action, 'save' ) ) {
if( $entity->_is_valid() ) {
$token = $this->session->pushToken();
$this->view->set( $this->session->popTokenTagName(), $token );
$this->view->set( 'action', 'save' );
}
$this->view->showForm_confirm( $entity );
return;
}
// save the entity.
if( $this->session->verifyToken() && $this->contextSaveOnlyOnce( $entity, $action, 'save' ) ) {
// saved just now.
$this->view->set( 'alert-success', 'your friendship has been saved. ' );
}
else {
// already saved.
$this->view->set( 'alert-info', 'your friendship has already been saved. ' );
}
// show done view.
$this->view->showForm_done( $entity );
return;
}
/**
* wizard-like interactions for inserting an entity. steps are:
* wizard1 -> wizard2 -> wizard3 -> confirm -> save/done.
*
* @param string $action
*/
public function saveWizards( $action )
{
// get entity from saved in the session.
$entity = $this->restoreData( 'entity' );
if( !$entity ) {
$this->clearData();
$entity = $this->role->em->newEntity( 'Interaction\model' );
$this->registerData( 'entity', $entity );
}
// show the wizard form. load and validates the input for each form.
$forms = array( 'wizard1', 'wizard2', 'wizard3', );
if( $form = $this->formWizards( $entity, $action, $forms ) ) {
$this->view->set( 'action', $form );
$this->view->showForm( $entity, $form );
return;
}
// show the confirm view. save token as well.
if( $this->contextValidateUnless( $entity, $action, 'save' ) ) {
$token = $this->session->pushToken();
$this->view->set( $this->session->popTokenTagName(), $token );
$this->view->set( 'action', 'save' );
$this->view->showForm( $entity, 'confirm' );
return;
}
// save the entity.
if( $this->session->verifyToken() && $this->contextSaveOnlyOnce( $entity, $action, 'save' ) ) {
$this->view->set( 'alert-success', 'your friendship has been saved. ' );
}
else {
$this->view->set( 'alert-info', 'your friendship has already been saved. ' );
}
// show done view.
$this->view->showForm( $entity, 'done' );
return;
}
// +----------------------------------------------------------------------+
}
<?php
namespace wsModule\Alt\Web;
/**
* Interaction for web input and output, such as forms.
* This is a DCI inspired module. things works as coded but still quite experimental.
*
* TODO: remove $this->view property. Interaction should not know about view...
*/
class Interaction
{
/** @var array data to register as session data */
protected $registeredData = array();
/** @var \wsModule\Alt\Web\Request */
protected $request;
/** @var \WScore\Web\Session saves itself and token for CSRF */
protected $session;
/** @var \WScore\DbAccess\Role */
protected $role;
public $showForm = 'showForm';
public $loadData = 'loadData';
public $actionName = 'action';
public $action = null;
// +----------------------------------------------------------------------+
// object management
// +----------------------------------------------------------------------+
/**
* @param \wsModule\Alt\Web\Request $request
* @param \WScore\Web\Session $session
* @param \WScore\DbAccess\Role $role
* @DimInjection Get \wsModule\Alt\Web\Request
* @DimInjection Fresh Session
* @DimInjection Get \WScore\DbAccess\Role
*/
public function __construct( $request, $session, $role )
{
$this->request = $request;
$this->session = ($session) ?: $_SESSION;
$this->role = $role;
}
/**
* load itself from session
*
* @throws \RuntimeException
* @return mixed
*/
public function loadRegistered()
{
$class = self::getInstanceName( get_called_class() );
if( $src = $this->session->get( $class ) ) {
$object = unserialize( $src );
$this->registeredData = $object;
return $object;
}
throw new \RuntimeException( 'Object not saved: '.$class );
}
/**
* saves the instance to session.
*/
public function saveRegistered() {
$class = self::getInstanceName( get_called_class() );
$this->session->set( $class, serialize( $this->registeredData ) );
}
/**
* @param $class
* @return mixed
*/
protected static function getInstanceName( $class ) {
$class = str_replace( '\\', '__', $class );
return $class;
}
/**
* @param string $controller
* @param string $action
*/
public function run( $controller, $action )
{
$this->$controller( $action );
$this->saveRegistered();
}
/**
* @param string $controller
* @param null|string $default
*/
public function action( $controller, $default=null )
{
$action = $default;
if( array_key_exists( $this->actionName, $_REQUEST ) ) {
$action = $_REQUEST[ $this->actionName ];
$this->loadRegistered();
}
$this->$controller( $action );
$this->saveRegistered();
}
// +----------------------------------------------------------------------+
// manage variables
// +----------------------------------------------------------------------+
/**
* @param string $name
* @param mixed $data
*/
public function registerData( $name, $data ) {
$this->registeredData[ $name ] = $data;
}
/**
* @param string $name
* @return mixed
*/
public function restoreData( $name ) {
return \WScore\Utilities\Tools::getKey( $this->registeredData, $name );
}
/**
* clears registered data
*/
public function clearData() {
$this->registeredData = array();
}
public function pinPoint( $name ) {
$pinPoint = '_pin_' . $name;
$this->registerData( $pinPoint, true );
}
public function checkPin( $name )
{
$pinPoint = '_pin_' . $name;
$pinned = false;
if( $this->restoreData( $pinPoint ) ) {
$pinned = true;
}
$this->pinPoint( $name );
return $pinned;
}
// +----------------------------------------------------------------------+
// contexts... a generic method for common use cases.
// +----------------------------------------------------------------------+
/**
* a context to show form and load post data from the form.
* returns true if $action is in this context, otherwise
* returns false.
*
* @param \WScore\DbAccess\Entity_Interface $entity
* @param string $action
* @param string $form
* @param string|null $prevForm
* @return bool
*/
public function contextFormAndLoad( $entity, $action, $form, $prevForm=null )
{
$role = $this->role->applyLoadable( $entity );
$isPost = $this->request->isPost();
// show form at least once. check for pin-point.
if ( !$this->checkPin( $form ) ) {
// no validation result is necessary when showing the form.
$role->resetValidation( true );
return true;
}
// requesting for a form.
if ( $action == $prevForm || ( $action == $form && !$isPost ) ) {
// no validation result is necessary when showing the form.
$role->resetValidation( true );
return true;
}
// load data if it is a post for a form.
if ( $action == $form && $isPost ) {
$loadData = $this->loadData;
$role->$loadData( $form );
}
// validate data *always*.
if ( !$role->validate( $form ) ) {
return true;
}
// all pass. not in this context.
return false;
}
/**
* for mostly showing confirm view. validates, again, and pushes token.
* returns true if $action is in this context, otherwise
* returns false.
*
* @param \WScore\DbAccess\Entity_Interface $entity
* @param string $action
* @param string $form
* @return bool
*/
public function contextValidateUnless( $entity, $action, $form )
{
$role = $this->role->applyLoadable( $entity );
// validate data *always*.
if ( !$role->validate() ) {
return true;
}
if( $action != $form ) {
return true;
}
return false;
}
/**
* saves the entity if $action is $form and token is verified. pin points the $form.
* returns true if $action is in this context (i.e. entity is saved), otherwise
* returns false.
*
* @param \WScore\DbAccess\Entity_Interface $entity
* @param string $action
* @param string $form
* @return bool
*/
public function contextSaveOnlyOnce( $entity, $action, $form )
{
// check if already saved.
if( $this->checkPin( $form ) ) return false;
// it's new. and further check the token.
if( $action == $form )
{
$role = $this->role->applyActive( $entity );
$role->save();
$this->pinPoint( $form );
return true;
}
return false;
}
// +----------------------------------------------------------------------+
// wizards and helpers for using context.
// +----------------------------------------------------------------------+
/**
* shows form, load and validates input data.
* returns the form name if action is in this context, and false if not.
*
* @param \WScore\DbAccess\Entity_Interface $entity
* @param string $action
* @param array $forms
* @return bool|string
*/
public function formWizards( $entity, $action, $forms )
{
$prevForm = null;
foreach( $forms as $form ) {
if( $this->contextFormAndLoad( $entity, $action, $form, $prevForm ) ) {
return $form;
}
$prevForm = $form;
}
return false;
}
// +----------------------------------------------------------------------+
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment