Can You check why filtering is not working in CGridView? When i type for exaple 'Adam' in filter field, nothing happens. I can't find my mistake, everything looks ok but not working. I helped with that article: Yii: CGridView Filter examples
CONTROLLER
<?php
class UzytkownikController extends CController
{
public function actionIndex()
{
$Dane = new Uzytkownik('search');
$Dane -> unsetAttributes(); // clear any default values
if(isset($_GET['Uzytkownik']))
{
$Dane->attributes=$_GET['Uzytkownik'];
}
$this -> render ('index', array(
'Dane' => $Dane,
));
}
}
?>
MODEL
<?php
class Uzytkownik extends CActiveRecord
{
public static function model($className=__CLASS__)
{
return parent::model($className);
}
public function search()
{
$criteria = new CDbCriteria;
$criteria -> compare('imie', $this -> imie, true);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
)
);
}
}
?>
WIEV
<?php
$this -> widget('zii.widgets.grid.CGridView', array(
'dataProvider' => $Dane -> search(),
'filter' => $Dane,
'columns' => array(
array(
'name' => 'imie',
'type'=>'raw',
),
array(
'name' => 'nazwisko',
'type'=>'raw',
'filter' => false,
),
array(
'name' => 'data',
'filter' => false,
),
),
)
);
?>
For future reference:
In order to make sure the $model->attributes "saves" the attributes the model needed the following addition:
public function rules() {
return array(
array('imie', 'safe', 'on'=>'search')
);
}
And $_GET should have been used instead of $_POST Because the CGridView widget uses GET when posting to the server:
class UzytkownikController extends CController
{
public function actionIndex()
{
$Dane = new Uzytkownik('search');
$Dane -> unsetAttributes(); // clear any default values
if(isset($_GET['Uzytkownik']))
{
$Dane->attributes=$_GET['Uzytkownik'];
}
$this -> render ('index', array(
'Dane' => $Dane,
));
}
}
Related
I am trying to implement a dropdown filter in Cgridview, The column is from another table.
//model search code
//person table
public function search(){
$criteria=new CDbCriteria;
$criteria->alias='per';
$criteria->compare('LastName',$this->LastName,true);
$criteria->compare('FirstName',$this->FirstName,true);
$criteria->join='right JOIN person_surveys ps ON ps.id = per.P_Id';
}
//relation to person survey
public function relations()
{
return array(
'user' => array(self::BELONGS_TO, 'PersonSurvey', 'P_Id'),
);
}
//person survey table model search
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('person_id',$this->person_id,true);
$criteria->compare('survey_ids',$this->survey_ids,true);
$criteria->join='left JOIN persons ps ON ps.id = per.P_Id';
}
//controller
public function actiongriddisplay(){
$model2=new Persons('search');
if(isset($_GET['ajax'])){
$model2->attributes =$_GET['Persons'];
$this->render('griddisplay',array('model2'=>$model2));
}
else{
$this->render('griddisplay',array('model2'=>$model2));
}
}
//in view file
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider' => $model2->Search(),
'filter' => $model2,
'ajaxType'=>'GET',
// 'ajaxUpdate'=>'items',
//'ajaxUrl' =>$this->createUrl('userManagement'),
'columns' => array(
array(
'name' => 'FirstName',
'header'=>'Full Name',
),
array(
'name' => 'user.survey_ids',
'header'=>'Survey ids',
'filter'=>CHtml::activeDropDownList($model2 ,'survey_ids', array('224'=>'224','223'=>'223','225'=>'225')),
'value'=>'isset($data->user->survey_ids)?$data->user->survey_ids:null'
),
I have put the search on, in models. why is the dropdown filter not working?
Why this is not working please help?
This link (http://www.yiiframework.com/wiki/281/searching-and-sorting-by-related-model-in-cgridview/) will solve your problem.
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
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.
I'm new to Yii, and I'm trying to do my initial project the "right" way. I've created a CFormModel class that needs three fields to query for some data, a CForm config to construct the form, and a CController to tie it together (all given below).
The data request needs an account, and this can come from a couple of different places. I think retrieving it should be in the controller. However, I don't know how to get it into the form's hidden "account" field from the controller, so that it makes it to the arguments assigned to the CFormModel after submission. More generally, I know how to pass from CController to view script, but not to CForm. Is the registry (Yii::app()->params[]) my best bet?
I suppose I can just leave it out of the form (and required fields) and wait to populate it in the submit action (actionSummaries). Does that break the intention of CForm? Is there a best practice? Even taking this solution, can someone address the first issue, in case it comes up again?
Any other, gentle critique is welcome.
models/SummariesForm.php
class SummariesForm extends CFormModel
{
public $account;
public $userToken;
public $year;
public function rules () {...}
public function fetchSummary () {...}
static public function getYearOptions () {...}
}
views/account/select.php
<?php
$this->pageTitle=Yii::app()->name;
?>
<div class="form">
<?php echo $form->render(); ?>
</div>
controllers/AccountController.php
class AccountController extends CController
{
public $layout = 'extranet';
public function actionSelect ()
{
$model = new SummariesForm();
// retrieve account
require_once 'AccountCookie.php';
/*
*
* Here, I insert the account directly into the
* model used to build the form, but $model isn't
* available to selectForm.php. So, it doesn't
* become part of the form, and this $model doesn't
* persist to actionSummaries().
*
*/
$model->account = AccountCookie::decrypt();
if ($model->account === false) {
throw new Exception('Unable to retrieve account.');
}
$form = new CForm('application.views.account.selectForm', $model);
$this->render('select', array(
'form' => $form,
'account' => $model->account,
));
}
public function actionSummaries ()
{
$model = new SummariesForm();
if (isset($_POST['SummariesForm'])) {
$model->attributes = $_POST['SummariesForm'];
/*
*
* Should I just omit "account" from the form altogether
* and fetch it here? Does that break the "model"?
*
*/
if ($model->validate() === true) {
try {
$summaries = $model->fetchSummary();
} catch (Exception $e) {
...
CApplication::end();
}
if (count($summaries) === 0) {
$this->render('nodata');
CApplication::end();
}
$this->render('summaries', array('model' => $model, 'summaries' => $summaries));
} else {
throw new Exception('Invalid year.');
}
}
}
}
views/account/selectForm.php
<?php
return array(
'title' => 'Select year',
'action' => Yii::app()->createUrl('Account/Summaries'),
'method' => 'post',
'elements' => array(
'account' => array(
'type' => 'hidden',
'value' => $account,
),
'userToken' => array(
'type' => 'hidden',
'value' => /* get token */,
),
'year' => array(
'type' => 'dropdownlist',
'items' => SummariesForm::getYearOptions(),
),
),
'buttons' => array(
'view' => array(
'type' => 'submit',
'label' => 'View summaries',
),
),
);
The answer is NO to do what you asked. You can see $form variable which acted almost like array when it was passed from controller to view. The solution is you add more property $account into selectForm model and treat it like other elements. I don't think leaving the new field outside the form will be properly way if you want to submit its value also.
Edited:
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;
}