I'm trying to limit my pagination results by the $this->Session->read('Player.team_id')... so that the logged in user can only see his related team members.
PlayersController.php
public $paginate = array(
'conditions' => array(
'Player.team_id' => $this->Session->read('Player.0.Team.id')
),
'limit' => 20,
'order' => array('Player.fullname' => 'asc')
);
public function index() {
$this->Paginator->settings = $this->paginate;
$this->Player->recursive = 0;
$this->set('players', $this->Paginator->paginate());
}
This causes an error when viewing player/index
Error: syntax error, unexpected T_VARIABLE
File: /home/www/public_html/dev/app/Controller/PlayersController.php
Line: 21
If I hardcode the 'conditions' as below then it works fine and only retrieves the records I want
'conditions' => array('Player.team_id' => 1)
In the Player.php model login action it writes the Session Variable Team.id and Team.name.
I have used the $this->Session->read else where in my app (views and other models) and it works fine. It just doesn't seem to work with in the pagination component?
This is simply invalid PHP syntax, class members can only be initialized with constant values, that is values that can be evaluated at compile time (strings, numbers, booleans, arrays, etc...)
Assign the session value at runtime in the Controller::beforeFilter() callback (or even directly in the index() action in case appropriate) instead:
public $paginate = array(
'limit' => 20,
'order' => array('Player.fullname' => 'asc')
);
public function beforeFilter() {
parent::beforeFilter();
$this->paginate['conditions'] = array(
'Player.team_id' => $this->Session->read('Player.0.Team.id')
);
}
Also as pointed out in the comments, make sure that the session key you are accessing actually exists and holds the expected value!
See also http://www.php.net/manual/en/language.oop5.properties.php
Related
this is the content of the autoload/global.php file :
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=web_builder;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
this is the content of the autoload/local.php file:
return array(
'db' => array(
'username' => 'DB_User_Name',
'password' => 'DB_Password',
,
); )
this is part of the content of module/Module.php :
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Zend\Db\Adapter\Adapter;
.........
public function getServiceConfig() {
return array(
'factories' => array(
'Application\Controller\UserController' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new Model\StickyNotesTable($dbAdapter);
return $table;
},
),
);
}
Here I really don't understand what this function do, I just copy pasted from an example. If you could explain me what does getServiceConfig function do, I will really appreaciate it.
Finally the controller content:
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Db\Adapter\Adapter;
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\Sql\Select;
class UserController extends AbstractActionController{
public function __construct(Adapter $adapter) {
$this->adapter = $adapter;
}
public function loginAction(){
// here i just want to a simple select and yes I know queries will be executed
//in Model, but I want to here a simple query.
//For example in Codeigniter I can't do in model, controller, or view as well.
return new ViewModel();
}
The result of all this code is obviously an error:
Catchable fatal error: Argument 1 passed to Application\Controller\UserController::__construct() must be an instance of Zend\Db\Adapter\Adapter, none given, called in C:\xampp\htdocs\zf2\vendor\zendframework\zendframework\library\Zend\ServiceManager\AbstractPluginManager.php on line 170 and defined in C:\xampp\htdocs\zf2\module\Application\src\Application\Controller\UserController.php on line 21
Can someone post an answer to make this database or query stuff work ? thx
Containable is not behaving as I expect in CakePHP 2.1.2 in the following code:
class ReportCard extends AppModel {
....
// debug shows expected results
public function test1(){
$this->Behaviors->attach('Containable');
$params = array(
'conditions' => array('ReportCard.id' => 1),
'contain' => array(
'ReportCardGradingPeriodCollection' => array(
'GradingPeriodCollection')));
debug($this->find('first', $params));
$this->Behaviors->detach('Containable');
}
// Unexpected: debug shows same array as test1
public function test2(){
$this->Behaviors->attach('Containable');
$params = array(
'conditions' => array('ReportCard.id' => 1),
'contain' => array(
'ReportCardGradingPeriodCollection' => array(
'GradingPeriodCollection' => array(
'GradingPeriodCollectionDetail' => array(
'GradingPeriod')))));
debug($this->find('first', $params));
$this->Behaviors->detach('Containable');
}
}
I get unexpected results when calling the functions from the controller. test1() shows the expected array. test2() shows the same array from test1. If I run test2() then test1() I get expected results (large array, then small array).
class ReportCardsController extends AppController {
....
public function test(){
$this->ReportCard->test1();
$this->ReportCard->test2();
}
}
I have tried using actAs in the model instead of dynamically loading the behavior in each function, but that did not help.
I apologize if I'm missing something simple. Thanks in advance!
Quoting the manual:
Containable must to be attached to all models used in containment,
From your example code it's not clear whether you are doing that.
And here's the remaining half of that note:
you may consider attaching it to your AppModel.
I currently have this in my Model (Referer Model):
public function getReferers($type = 'today') {
if ($type == 'this_month') {
return $this->_getThisMonthsReferers();
} elseif ($type == 'today') {
return $this->_getTodaysPageReferers();
}
}
private function _getThisMonthsReferers() {
$today = new DateTime();
return $this->Visitor->find('all', array(
'fields' => array(
'Referer.url',
'COUNT(UserRequest.visitor_id) as request_count',
'COUNT(DISTINCT(Visitor.id)) as visitor_count',
'COUNT(UserRequest.visitor_id) / COUNT(DISTINCT(Visitor.id)) as pages_per_visit',
'COUNT(DISTINCT(Visitor.id)) / COUNT(UserRequest.visitor_id) * 100 as percent_new_visit'
),
'joins' => array(
array(
'table' => 'user_requests',
'alias' => 'UserRequest',
'type' => 'RIGHT',
'conditions' => array(
'UserRequest.visitor_id = Visitor.id'
)
)
),
'conditions' => array(
'Visitor.site_id' => $this->Site->id,
'MONTH(UserRequest.created)' => $today->format('m'),
'YEAR(UserRequest.created)' => $today->format('Y')
),
'group' => array(
'url'
)
));
}
The thing is that I how I would paginate this. It will be so easy if just copy my code out of the model and to the controller. The thing is I want the keep the query in my Model.
How is this supposed to be done in CakePHP?
A custom find type is one method. You can find more information here: http://book.cakephp.org/2.0/en/core-libraries/components/pagination.html#custom-query-pagination
To turn your _getThisMonthsReferers into a custom find, follow this http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#creating-custom-find-types
For example:
// model
protected function _findThisMonthsReferers($state, $query, $results = array()) {
if ($state === 'before') {
$query['fields'] = ....
$query['joins'] = ....
return $query;
}
return $results;
}
// controller
public $paginate = array('findType' => 'thisMonthsReferers')
EDIT:
I think it should be :
public $paginate = array('thisMonthsReferers');
However the Solution I used derived from this answer is adding this to the method I am using
$this->paginate = array('thisMonthsReferers');
Since I don't want i used in all my actions. Then paginating the Model like this.
$this->paginate('Visitor);
Instead of returning the results of the find, just return it's array of options:
return array(
'fields' => array(
//...etc
Then use those options to paginate in the controller. More details on this answer of this similar question: Paginate from within a model in CakePHP
It still keeps the model fat (with any logic that might alter the conditions, joins, fields...etc), and the controller skinny, which just uses the returned array as paginate options.
I have a model Post that has many association with the model Comment.
Post has a primary key post_id which is Comment s foreign key.
Both of these have a visible column.
I have a working query on Post.visible options, and I need to add the AND to find all Posts that have one of Post.visible values.
For these posts I need all Comments that have a Comment.visible value = 1.
My code:
$conditions = array(
"OR" => array(
"Post.visible" => array(
1,
2,
3,
4
),
),
"AND" => array (
"Comment.visible" => 1
)
);
$result = $this->Post->find('all', array(
'order' => 'Post.created DESC',
'conditions' => $conditions
));
The result without the AND is OK (but I get also the Comments with visible = 0).
When I put the condition "Comment.visible" => 1 in the has manyassociation, it works well (but I can not do this, because I need to get the Comment with visibility 0 elsewhere).
With the and it shows this Error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Comment.visible' in 'where clause'
When I dump the SQL, the comments table is not even matched in the SELECT clause (nor in the LEFT JOIN).
You can limit another model's results using CakePHP's Containable Behavior with something like this (this should work, but feel free to tweak per your needs):
//Post model
public $recursive = -1;
public $actsAs = array('Containable');
public function getPosts() {
$posts = $this->find('all',
array(
'conditions' => array(
'Post.visible' => 1
),
'contain' => array(
'Comment' => array(
'conditions' => array('Comment.visible' => 1)
)
)
)
);
return $posts;
}
Or, you can set up your association to only ever pull comments that are visible (even WITH this way, I still recommend using 'contain' like above - you just wouldn't need to specify the condition each time):
//Post model
public $hasMany = array(
'Comment' => array(
'conditions' => array('Comment.visible' => 1)
)
);
I am trying to do a search, using pagination for posts which have a specific tag or tags (for example, if a user was to select two tags, then posts containing either tag would be returned).
I have the relationship defined in my Posts table
public $hasAndBelongsToMany = array('Tags' => array(
'className' => 'Tags',
'joinTable' => 'posts_tags',
'foreignKey' => 'post_id',
'associationForeignKey' => 'tag_id',
'unique' => 'keepExisting'));
How do I use Find to retrieve rows with a given tag (name or ID would be fine)
Trying:
// other pagination settings goes here
$this->paginate['conditions']['Tags.id'] = 13;
gives me an error that the relationship does not exist.
Looking at the debug info it appears that the tables are not joining the Posts_Tags and Tags table, however, when I debug the data making it to the view, the Posts objects contain the tags data.
Most of the documentation I can find for this seems to revolve around earlier versions of CakePHP, any help would be appreciated.
Could not find a satisfying solution myself.
I created a behavior to take care of this.
Create a file called HabtmBehavior.php and put it in your app/Model/Behavior folder.
Put the block of code in there and save file.
Add the behavior to your model: eg public $actsAs = array('Habtm');
Here is a usage example with find.
<?php $this->Entry->find('all', array('habtm'=>array('Tag'=>array('Tag.title'=>'value to find'))) ?>
Paginate would look something like this:
$this->paginate['Entry']['habtm']['Tag'] = array('Tag.title'=>'value to find');
You are free to add as many relations as you want by adding additional Model Names in the habtm array.
(Just be careful not to make it to complex since this could start slowing down your find results.)
<?php
class HabtmBehavior extends ModelBehavior {
public function beforeFind(Model $model, $options) {
if (!isset($options['joins'])) {
$options['joins'] = array();
}
if (!isset($options['habtm'])) {
return $options;
}
$habtm = $options['habtm'];
unset($options['habtm']);
foreach($habtm as $m => $scope){
$assoc = $model->hasAndBelongsToMany[$m];
$bind = "{$assoc['with']}.{$assoc['foreignKey']} = {$model->alias}.{$model->primaryKey}";
$options['joins'][] = array(
'table' => $assoc['joinTable'],
'alias' => $assoc['with'],
'type' => 'inner',
'foreignKey' => false,
'conditions'=> array($bind)
);
$bind = $m.'.'.$model->{$m}->primaryKey.' = ';
$bind .= "{$assoc['with']}.{$assoc['associationForeignKey']}";
$options['joins'][] = array(
'table' => $model->{$m}->table,
'alias' => $m,
'type' => 'inner',
'foreignKey' => false,
'conditions'=> array($bind) + (array)$scope,
);
}
return $options;
}
}
Hope this helps.
Happy baking.
I think the best solution is apply find function on join table Model. I try this before and it's work fine.
in your PostTag model :
/**
* #see Model::$actsAs
*/
public $actsAs = array(
'Containable',
);
/**
* #see Model::$belongsTo
*/
public $belongsTo = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'post_id',
),
'Tags' => array(
'className' => 'Tag',
'foreignKey' => 'tag_id',
),
);
in your controller :
// $tagsId = tags ids
$posts = $this->PostTag->find('all', array('conditions' => array('PostTag.tag_id' => $tagsId),'contain' => array('Post')));
also is better follow cake naming convention, if you have tags(plural), post_tags(first singular second plural),posts(plural) tables you must have Tag,PostTag,Post Models.