is_user_logged_in() return false even when logged in to WordPress? - api

I have a plugin that I created and I want to use the WP rest api controller pattern and extend the api.
<?php
/**
* Plugin Name: myplugin
* Plugin URI: h...
* Description: A simple plugin ...
* Version: 0.1
* Author: Kamran ...
* Author ....
* License: GPL2
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items();
$items->register_routes();
}
add_action('rest_api_init','myplugin_register_endpoints');
.
.
I created a class in folder called server/controllers and inside it my_ctrl.php file with a class that extends WP_REST_Controller that looks like this
<?php
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {
$version = '1';
$namespace = 'my-namespase/v' . $version;
$base = 'abc';
register_rest_route( $namespace, '/' . $base, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
) );
register_rest_route( $namespace, '/' . $base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'create_item' ),
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_item' ),
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
) );
register_rest_route( $namespace, '/' . $base . '/schema', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_public_item_schema' ),
) );
}
function get_items( $request ){
return new WP_REST_Response( array('message' => "list items"), 200 );
}
function create_item( $request ) {
.....
if($author_email==$user_email) {
return new WP_REST_Response( array('message' => 'success', 200 );
} else {
return new WP_Error('my-error', __(' error...','abc'), array( 'status' => 500 ));
}
}
//Remove vote////////////////////////////////////////////
function delete_item( $request ) {
...
if($author_email==$user_email) {
return new WP_REST_Response( array('message' => 'success', 200 );
} else {
return new WP_Error('my-error', __(' error...','abc'), array( 'status' => 500 ));
}
}
public function get_items_permissions_check( $request ) {
return true;
}
public function create_item_permissions_check( $request ) {
if ( !is_user_logged_in()) {
return new WP_Error('login error',__('You are not logged in','KVotes-voting'));
}
return true;
}
public function delete_item_permissions_check( $request ) {
return $this->create_item_permissions_check( $request );
}
protected function prepare_item_for_database( $request ) {
return array();
}
public function prepare_item_for_response( $item, $request ) {
return array();
}
public function get_collection_params() {
return array(
'page' => array(
'description' => 'Current page of the collection.',
'type' => 'integer',
'default' => 1,
'sanitize_callback' => 'absint',
),
'per_page' => array(
'description' => 'Maximum number of items to be returned in result set.',
'type' => 'integer',
'default' => 10,
'sanitize_callback' => 'absint',
),
'search' => array(
'description' => 'Limit results to those matching a string.',
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
);
}
}
I am logged in and I am using cookie authentication with Nonce in my plugin.
when I run my code and debug it with sublime xdebug extension I can see that I indeed hit the end points routes but although I am logged it in the lines: "is_user_logged_in()" = (bool) 0 and therefore the function create_item_permissions_check return new WP_Error(....);and not true;
therefore my rest callback "create_item" is not invoked, I don't understand why is_user_logged_in() return false even when I am logged in.

The solution was to send the logged in user info to my custom class as a parameter to the constructor and then use the user data in the permission check function and other functions that needs the user info:
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
private $loged_in;//bool
private $user;
public function __construct($logged,$cur_user) {
= $logged;
$this->user = $cur_user;
}
.
.
.
public function create_item_permissions_check( $request ) {
if($this->loged_in!=1){
return new WP_Error('login error',__('You are not logged in','....'));
}
return true;
}
.
.
.
}
And my plugin myplugin_register_endpoints looks as follows:
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items(is_user_logged_in(),wp_get_current_user());
$items->register_routes();
}
now when I route to one of the URL's And hit the end points and the check permission is invoked with the needed user data. $this->loged_in!=1 when thew user is not logged in, otherwise the permission check returns true .

I ran into precisely this issue. It seems that the architecturally correct way to handle the problem is for the ajax request to include a nonce. See the references discussed in this answer

Related

Create form in back office by module Prestashop 1.7

I made a module to hook a form in product page in the back office (with hook DisplayAdminProductExtra).
How can I create a form with some inputs by module?
I think it can be done by {helper and .tpl file} or {form_field and .twig file}.
If anyone explains this as a walkthrough I'm sure it's gonna be a good reference for many others too.
this is the code that created by PrestaShop module generator:
<?php
if (!defined('_PS_VERSION_')) {
exit;
}
class Myfirstmodule extends Module
{
protected $config_form = false;
public function __construct()
{
$this->name = 'myfirstmodule';
$this->tab = 'administration';
$this->version = '1.0.0';
$this->author = 'parsa';
$this->need_instance = 0;
/**
* Set $this->bootstrap to true if your module is compliant with bootstrap (PrestaShop 1.6)
*/
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('new module');
$this->description = $this->l('first module');
$this->confirmUninstall = $this->l('Are you sure?');
$this->ps_versions_compliancy = array('min' => '1.7', 'max' => _PS_VERSION_);
}
/**
* Don't forget to create update methods if needed:
* http://doc.prestashop.com/display/PS16/Enabling+the+Auto-Update
*/
public function install()
{
Configuration::updateValue('MYFIRSTMODULE_LIVE_MODE', false);
return parent::install() &&
$this->registerHook('header') &&
$this->registerHook('backOfficeHeader') &&
$this->registerHook('displayAdminProductsExtra');
}
public function uninstall()
{
Configuration::deleteByName('MYFIRSTMODULE_LIVE_MODE');
return parent::uninstall();
}
/**
* Load the configuration form
*/
public function getContent()
{
/**
* If values have been submitted in the form, process.
*/
if (((bool)Tools::isSubmit('submitMyfirstmoduleModule')) == true) {
$this->postProcess();
}
$this->context->smarty->assign('module_dir', $this->_path);
$output = $this->context->smarty->fetch($this->local_path . 'views/templates/admin/configure.tpl');
return $output . $this->renderForm();
}
/**
* Create the form that will be displayed in the configuration of your module.
*/
protected function renderForm()
{
$helper = new HelperForm();
$helper->show_toolbar = false;
$helper->table = $this->table;
$helper->module = $this;
$helper->default_form_language = $this->context->language->id;
$helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG', 0);
$helper->identifier = $this->identifier;
$helper->submit_action = 'submitMyfirstmoduleModule';
$helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false)
. '&configure=' . $this->name . '&tab_module=' . $this->tab . '&module_name=' . $this->name;
$helper->token = Tools::getAdminTokenLite('AdminModules');
$helper->tpl_vars = array(
'fields_value' => $this->getConfigFormValues(), /* Add values for your inputs */
'languages' => $this->context->controller->getLanguages(),
'id_language' => $this->context->language->id,
);
return $helper->generateForm(array($this->getConfigForm()));
}
/**
* Create the structure of your form.
*/
protected function getConfigForm()
{
return array(
'form' => array(
'legend' => array(
'title' => $this->l('Settings'),
'icon' => 'icon-cogs',
),
'input' => array(
array(
'type' => 'switch',
'label' => $this->l('Live mode'),
'name' => 'MYFIRSTMODULE_LIVE_MODE',
'is_bool' => true,
'desc' => $this->l('Use this module in live mode'),
'values' => array(
array(
'id' => 'active_on',
'value' => true,
'label' => $this->l('Enabled')
),
array(
'id' => 'active_off',
'value' => false,
'label' => $this->l('Disabled')
)
),
),
array(
'col' => 3,
'type' => 'text',
'prefix' => '<i class="icon icon-envelope"></i>',
'desc' => $this->l('Enter a valid email address'),
'name' => 'MYFIRSTMODULE_ACCOUNT_EMAIL',
'label' => $this->l('Email'),
),
array(
'type' => 'password',
'name' => 'MYFIRSTMODULE_ACCOUNT_PASSWORD',
'label' => $this->l('Password'),
),
),
'submit' => array(
'title' => $this->l('Save'),
),
),
);
}
/**
* Set values for the inputs.
*/
protected function getConfigFormValues()
{
return array(
'MYFIRSTMODULE_LIVE_MODE' => Configuration::get('MYFIRSTMODULE_LIVE_MODE', true),
'MYFIRSTMODULE_ACCOUNT_EMAIL' => Configuration::get('MYFIRSTMODULE_ACCOUNT_EMAIL', 'contact#prestashop.com'),
'MYFIRSTMODULE_ACCOUNT_PASSWORD' => Configuration::get('MYFIRSTMODULE_ACCOUNT_PASSWORD', null),
);
}
/**
* Save form data.
*/
protected function postProcess()
{
$form_values = $this->getConfigFormValues();
foreach (array_keys($form_values) as $key) {
Configuration::updateValue($key, Tools::getValue($key));
}
}
/**
* Add the CSS & JavaScript files you want to be loaded in the BO.
*/
public function hookBackOfficeHeader()
{
if (Tools::getValue('module_name') == $this->name) {
$this->context->controller->addJS($this->_path . 'views/js/back.js');
$this->context->controller->addCSS($this->_path . 'views/css/back.css');
}
}
/**
* Add the CSS & JavaScript files you want to be added on the FO.
*/
public function hookHeader()
{
$this->context->controller->addJS($this->_path . '/views/js/front.js');
$this->context->controller->addCSS($this->_path . '/views/css/front.css');
}
public function hookDisplayAdminProductsExtra()
{
/* Place your code here. */
}
}
Welcome on Stack Overflow
using HelperForm on PrestaShop 1.7 product page is not really working at this moment, it is recommended to use HTML markup and get all values of the form from the $_POST using actionProductSave hook

CakePHP 2.6 isAuthorized not being called

I have a working project that i want to extend with authentication. I followed the Simple Authentication and Authorization Tutorial here.
Logging in works and i can print the Username etc. But the access control is not working. The Method isAuthorized is simply not working. What am i missing here?
Edit - I receive the below error;
"You are not authorized to access that location."
Part of my AppController:
public $components = array('Flash', 'RequestHandler', 'Cookie', 'Session', 'Auth' => array(
'loginRedirect' => array(
'controller' => 'status',
'action' => 'index'
),
'logoutRedirect' => array(
'controller' => 'user',
'action' => 'login'
),
'authenticate' => array(
'Form' => array(
'passwordHasher' => 'Blowfish'
),
),
'authorize' => array('Controller')
));
public function isAuthorized($user)
{
if (isset($user['role']) && $user['role'] === 'admin') return true;
return false;
}
Part of my taskController:
public function isAuthorized($user)
{
debug($user); die();
if ($this->action === 'index') return true;
if (in_array($this->action, array('edit', 'delete')))
{
$postId = (int) $this->request->params['pass'][0];
if ($this->Post->isOwnedBy($postId, $user['id'])) return true;
}
return parent::isAuthorized($user);
}
Part of UsersController:
public function login()
{
if ($this->request->is('post'))
{
if ($this->Auth->login())
{
return $this->redirect($this->Auth->redirectUrl()); // This is being called after login so it seems to work!
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}

form goes blank on use of TbExtendedGridView

I am new to yii1. In my project ,I have used TblExtendedGridView to display data in the table .The form shows the data in my local computer.But when the project is uloaded in server,the file is blank and doesnot show any error.
What is the problem?
'<?php
$uniqid=md5(uniqid());
$this->widget('bootstrap.widgets.TbExtendedGridView', array(
'id'=>'marketing-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'type' => 'striped bordered',
'type' => 'striped bordered condensed',
'columns'=>array(
array(
'header'=>'#',
'value'=>'$this->grid->dataProvider->pagination->currentPage * $this->grid->dataProvider->pagination->pageSize + ($row+1)',
),
array(
'name'=>'client_id',
'header'=>'Company',
'value'=>'$data->clientName->client_name',
'htmlOptions' => array('style'=>'width:200px'),
),
array(
'name'=>'client_contact_id',
'header'=>'Contacted',
'value'=>'$data->contactPerson->contact_name',
'htmlOptions' => array('style'=>'width:180px'),
),
array(
'name'=>'visited_date',
'header'=>'Visit Date',
'htmlOptions' => array('style'=>'width:100px'),
),
array(
'name'=>'possibility',
'header'=>'Probability',
'htmlOptions' => array('style'=>'width:100px'),
),
'remarks',
array(
'name'=>'next_visited_date',
'header'=>'Next Contact Date',
'htmlOptions' => array('style'=>'width:100px'),
),
array(
'name'=>'follow_up_by',
'header'=>'Follow Up By',
'value'=>'$data->followPerson->user_name',
'htmlOptions' => array('style'=>'width:180px'),
),
),
),
),
)); ?>'
'My controller is:
<?php
class MarketingController extends Controller
{
public $layout='//layouts/column1';
public function actionIndex()
{
$this->actionAdmin();
}
// Uncomment the following methods and override them if needed
public function filters()
{
return array(
'accessControl', // perform access control for CRUD operations
'postOnly + delete', // we only allow deletion via POST request
);
}
public function accessRules()
{
return array(
array('allow', // allow all users to perform 'index' and 'view' actions
'actions'=>array('index','view','DynamicContact'),
'users'=>array('*'),
),
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array('admin','delete','create','update','DynamicContact'),
'users'=>array('#'),
),
array('allow', // allow admin user to perform 'admin' and 'delete' actions
'actions'=>array('admin','delete','DynamicContact'),
'users'=>array('admin','#'),
),
array('deny', // deny all users
'users'=>array('*'),
),
);
}
protected function performAjaxValidation($model)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='marketing-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
}
public function actionView($id)
{
EQuickDlgs::render('view',array('model'=>$this->loadModel($id)));
}
public function actionAdmin()
{
$model=new Marketing('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['Marketing']))
$model->attributes=$_GET['Marketing'];
$this->render('admin',array(
'model'=>$model,
));
}
public function actionCreate()
{
$model=new Marketing;
// Uncomment the following line if AJAX validation is needed
$this->performAjaxValidation($model);
if(isset($_POST['Marketing']))
{
$model->attributes=$_POST['Marketing'];
//print_r($_POST['User']);
//die;
if($model->save())
{
EQuickDlgs::checkDialogJsScript();
$this->redirect(array('marketing/admin','id'=>$model->marketing_id));
}
}
EQuickDlgs::render('create',array(
'model'=>$model,
));
}
public function loadModel($id)
{
$model=Marketing::model()->findByPk($id);
if($model===null)
throw new CHttpException(404,'The requested page does not exist.');
return $model;
}
public function actionUpdate($id)
{
$model=$this->loadModel($id);
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);
if(isset($_POST['Marketing']))
{
$model->attributes=$_POST['Marketing'];
if($model->save())
{
//$this->redirect(array('view','id'=>$model->user_id));
EQuickDlgs::checkDialogJsScript();
$this->redirect(array('marketing/admin','id'=>$model->marketing_id));
}
}
EQuickDlgs::render('update',array(
'model'=>$model,
));
}
public function actionDelete($id)
{
$this->loadModel($id)->delete();
// if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser
if(!isset($_GET['ajax']))
$this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));
}
}'
'And Model is :
<?php
class Marketing extends PMActiveRecord
{
/**
* #return string the associated database table name
*/
public function tableName()
{
return 'pm_mar_marketing';
}
public function rules()
{
return array(
array('client_id, client_contact_id, follow_up_by, visited_date, visit_type, next_visited_date, possibility, remarks', 'required'),
array('client_id, client_contact_id, follow_up_by, visit_type, crtd_by, updt_by, updt_cnt', 'numerical', 'integerOnly'=>true),
array('possibility', 'length', 'max'=>20),
// The following rule is used by search().
// #todo Please remove those attributes that should not be searched.
array('marketing_id, client_id, client_contact_id, follow_up_by, visited_date, visit_type, next_visited_date, possibility, remarks', 'safe', 'on'=>'search'),
);
}
public function relations()
{
return array(
'clientName' => array(self::BELONGS_TO, 'Client', 'client_id'),
'contactPerson' => array(self::BELONGS_TO, 'ClientContact', 'client_contact_id'),
'followPerson' => array(self::BELONGS_TO, 'User', 'follow_up_by'),
'visitType' => array(self::BELONGS_TO, 'CodeValue', 'visit_type')
);
}
/**
* #return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'marketing_id' => 'Marketing',
'client_id' => 'Client',
// 'client_id'=> 'Client',
'client_contact_id' => 'Contact Person',
//'client_contact_id' => 'Contact Person',
'follow_up_by' => 'Follow Up By',
'visited_date' => 'Visited Date',
'visitType.code_lbl' => 'Visit Type',
'next_visited_date' => 'Next Contact Date',
'possibility' => 'Probability',
'remarks' => 'Remarks',
'crtd_by' => 'Crtd By',
'crtd_dt' => 'Crtd Dt',
'updt_by' => 'Updt By',
'updt_dt' => 'Updt Dt',
'updt_cnt' => 'Updt Cnt',
);
}
public function search()
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
$criteria->with = array('clientName','contactPerson','followPerson');
$criteria->compare('marketing_id',$this->marketing_id);
$criteria->compare('clientName.client_name',$this->client_id,true);
$criteria->compare('contactPerson.contact_name',$this->client_contact_id,true);
$criteria->compare('followPerson.user_name',$this->follow_up_by,true);
$criteria->compare('visited_date',$this->visited_date,true);
//$criteria->compare('visit_type',$this->visit_type);
$criteria->compare('next_visited_date',$this->next_visited_date,true);
$criteria->compare('possibility',$this->possibility,true);
$criteria->compare('remarks',$this->remarks,true);
$criteria->compare('crtd_by',$this->crtd_by);
$criteria->compare('crtd_dt',$this->crtd_dt,true);
$criteria->compare('updt_by',$this->updt_by);
$criteria->compare('updt_dt',$this->updt_dt,true);
$criteria->compare('updt_cnt',$this->updt_cnt);
//$criteria -> join = 'INNER JOIN pm_marketing_user followPerson on t.follow_up_by= followPerson.user_id';
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
public static function model($className=__CLASS__)
{
return parent::model($className);
}
}
'
If This happend only whit this view check for the lowercase/uppercase of the class code file (Marketing.php i guess)
If your development enviroment is windows and your produdction enviroment is Unix like, can happen yuo have marketing.php in your model and you referer to Marketing.php or to the contrary this work in windows/dos because in case insentisitve but not in Unix like O.S.

How to use the InputFilter in a nested mapper model class in Zend Framework 2?

Everyone, who started ZF2 learning with the "Get started" tutorial, will know the model class Album (s. below).
Now I want to extend my model with songs. One album can have 0 or more songs. The songs will get a new talbe songs (id, title, album_id) and the mapper Album\Model\Song. The mapper Album\Model\Song will be built similar to Album\Model\Album. The mapper Album\Model\Album will get a new property songCollection (array of Album\Model\Song objects or maybe something like Album\Model\SongCollection object).
Does it make sence to use the InputFilter for "nested" (mapper) classes?
How should the getInputFilter() be modified?
How should the setInputFilter() be modified? OK, now it is not implemented at all. But it's approximately clear how to do it for a shallow class structure -- and not clear how to implement it for a mapper, that references another mapper(-s).
Album\Model\Album
<?php
namespace Album\Model;
use Zend\Stdlib\ArraySerializableInterface;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Album implements InputFilterAwareInterface, ArraySerializableInterface {
public $id;
public $artist;
public $title;
protected $inputFilter;
public function exchangeArray(array $data) {
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
public function toArray() {
return $this->getArrayCopy();
}
public function getArrayCopy() {
return get_object_vars($this);
}
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new \Exception('Not used');
}
public function getInputFilter() {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int')
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'artist',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validarots' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100
)
)
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validarots' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100
)
)
)
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
I think you are a little confused with the relationship with the models and mappers set out in this example.
The 'mappers' would be the TableGateway objects, such as AlbumTable, SongTable etc. The Album and Song classes yo would call models, or Domain Objects, these are what represent the actual entities in your application. The Mappers just take care of persisting them in your database etc.
When using the TableGateway implementation, I would let each Domain Object (such as Ablum) handle the InputFilter for the attributes it's TableGateway is going to persist (such as AlbumTable).
For the example you stated, I would not change the Album Models InputFilter at all. The reason is the relationship with Songs is this:
Album HAS many songs, Song Belongs to Album (the Song would have the link back to the Album)
Add a new Song Object and Gateway:
<?php
namespace Album\Model;
use Zend\Stdlib\ArraySerializableInterface;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Song implements InputFilterAwareInterface, ArraySerializableInterface {
protected $id;
protected $album;
protected $title;
protected $inputFilter;
// Added Getters / Setters for the attributes rather than
// having public scope ...
public function setAlbum(Album $album)
{
$this->album = $album;
}
public function getAlbum()
{
return $this->album;
}
public function exchangeArray(array $data) {
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
if(isset($data['album_id'])) {
$album = new Album();
$album->exchangeArray($data['album_id']);
$this->setAlbum($album);
}
}
public function toArray() {
return $this->getArrayCopy();
}
public function getArrayCopy() {
return array(
'id' => $this->id,
'album_id' => $this->getAlbum()->id,
'title' => $this->title,
);
}
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new \Exception('Not used');
}
public function getInputFilter() {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int')
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'album_id',
'required' => true,
'filters' => array(
array('name' => 'Int')
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validarots' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100
)
)
)
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
Notice no need to change the Album Model as the relationship is 'Song Belongs to Album'.
When you object relationships get more complex you will want to look at using Hydrators to build the objects for you (http://framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.html)
Now you would create a SongTable to persist this new Object for you:
<?php
namespace Album\Model;
use Zend\Db\TableGateway\TableGateway;
class SongTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getSong($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
public function saveSong(Song $song)
{
$data = array(
'album_id' => $song->getAlbum()->id,
'title' => $song->title,
);
$id = (int)$song->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getSong($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function fetchAlbumSongs(Album $album)
{
$resultSet = $this->tableGateway->select(array(
'album_id' => $album->id
));
return $resultSet;
}
public function addSongsToAlbum(Album $album)
{
foreach($this->fetchAlbumSongs($album) as $song) {
$album->addSong($song);
}
}
}
You Could then Modify you Album model to allow Songs to be added:
class Album implements InputFilterAwareInterface, ArraySerializableInterface {
// Other stuff here
/**
* #var array
*/
protected $songs = array();
public function addSong(Song $song)
{
$this->songs[] = $song;
}
public function getSongs()
{
return $this->songs;
}
}
You can then build your object graph easily, I would usually make a server to do do this kind of thing:
AlbumService.php
public function getAlumbWithSongs(int $id)
{
$album = $this->getAlbumTable()->getAlbum($id);
if($album) {
$this->getSongTable()->addSongsToAlbum($album);
}
return $album;
}

Yii CForm, Nested Forms Ajax Validation

I have created the following nested forms array;
return array(
'elements' => array(
'contact' => array(
'type' => 'form',
'elements' => array(
'first_name' => array(
'type' => 'text',
),
'last_name' => array(
'type' => 'text',
)
),
),
'lead' => array(
'type' => 'form',
'elements' => array(
'primary_skills' => array(
'type' => 'textarea',
),
),
),
),
'buttons' => array(
'save-lead' => array(
'type' => 'submit',
'label' => 'Create',
'class' => 'btn'
),
)
);
i have view page like this
echo $form->renderBegin();
echo $form['lead'];
echo $form['contact'];
echo $form->buttons['save-lead'];
echo $form->renderEnd();
my actionCreate is like this
$form = new CForm('application.views.leads.register');
$form['lead']->model = new Lead;
$form['contact']->model = new Contact;
// how can i perform ajax validation only for $form['contact']
$this->performAjaxValidation($model);
//if contact form save btn is clicked
if ($form->submitted('save-lead') && $form['contact']->validate() &&
$form['lead']->validate()
) {
$contact = $form['contact']->model;
$lead = $form['lead']->model;
if ($contact->save()) {
$lead->contact_id = $contact->id;
if ($lead->save()) {
$this->redirect(array('leads/view', 'id' => $lead->id));
}
}
}
ajax validation method is
protected function performAjaxValidation($model)
{
if (isset($_POST['ajax']) && $_POST['ajax'] === 'contact') {
echo CActiveForm::validate($model);
Yii::app()->end();
}
}
so my question is how can i perform ajax validation on $form['contact'] and $form['lead'] elements individually?
You can have several forms in a page but they should not be nested.
Nested forms are invalid.
You should make your own validation
in actionCreate and actionUpdate of your controller you must add (i have main model "Invoice" and secondary "InvoiceDetails", and there could be more than 1 form for InvoiceDetails). But of course forms cannot be nested!
public function actionCreate()
{
...
$PostVar = 'Invoices';
if (Yii::app()->request->isAjaxRequest)
{ // if ajax
$this->performAjaxValidation($model, strtolower($PostVar) . '-form');
$PostVar = ucfirst($PostVar);
if (isset($_POST[$PostVar]))
{
$model->attributes = $_POST[$PostVar];
$dynamicModel = new InvoiceDetails(); //your model
$valid = self::validate($model, $dynamicModel);
if (!isset($_POST['ajax']))
{
if (isset($_POST['InvoiceDetails']))
{
$allDetails = array();
$allDynamicModels = $_POST['InvoiceDetails'];
//your own customization
foreach ($allDynamicModels as $key => $value)
{
$InvDet = InvoiceDetails::model()->findByPk($_POST['InvoiceDetails'][$key]['id']);
if (!isset($InvDet))
{
$InvDet = new InvoiceDetails();
}
$InvDet->attributes = $_POST['InvoiceDetails'][$key];
$InvDet->save();
$allDetails[] = $InvDet;
}
}
$model->invoicedetails = $allDetails;
if ($model->save())
{
echo CJSON::encode(array('status' => 'success'));
Yii::app()->end();
}
else
{
echo CJSON::encode(array('status' => 'error saving'));
Yii::app()->end();
}
}
// Here we say if valid
if (!isset($valid))
{
echo CJSON::encode(array('status' => 'success'));
}
else
{
echo $valid;
}
Yii::app()->end();
}
else
{
$this->renderPartial('_form', ...);
}
}
else
{
// not AJAX request
$this->render('_form', ...));
}
Nested Forms are invalid. You can use scenarios to validate the form at different instances.
Example:
`if ($form->submitted('save-lead'){
$form->scenario = 'save-lead';
if($form->validate()) {
$form->save();
}
} else {
$form->scenario = 'contact';
if($form->validate()){
$form->save();
}
}
$this->render('_form', array('form'=>$form);`