MANY_MANY relation does not work for me - many-to-many

I am now discovering the Yii framework and doing it by trying to develop a simple application which allows to create Leagues and assign Players to it. The relationship between these is many to many (a player can subscribe to many leagues, and one league contains many players). So I have three db tables - tbl_league, tbl_player and tbl_league_player and I created foreign keys like this:
ALTER TABLE tbl_league_player ADD FOREIGN KEY league_id_idxfk (league_id) REFERENCES tbl_league (id);
ALTER TABLE tbl_league_player ADD FOREIGN KEY player_id_idxfk (player_id) REFERENCES tbl_player (id);
Now I am trying to display League with the list of all players subscribed to it, so my actionView in LeagueController says:
public function actionView($id)
{
$issueDataProvider = new CActiveDataProvider('Player', array(
'criteria' => array(
'condition' => 'league_id=:leagueId',
'params' => array(
':leagueId' => $this->loadModel($id)->id
),
),
'pagination' => array(
'pageSize' => 1,
),
));
$this->render('view',array(
'model'=>$this->loadModel($id),
'issueDataProvider' => $issueDataProvider,
));
}
What I am trying to do here is to get all players from the Player table subscribed to a particular league ID. For this I have the Player model with relation defined like this:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'League' => array(self::MANY_MANY, 'League', 'tbl_league_player(player_id, league_id)'),
);
}
The problem is that I go to, for example, league/view&id=2, I am getting this error: "SELECT COUNT(*) FROM tbl_player t WHERE league_id=:leagueId" so it looks like the relation does not work. What am I missing?
Thanks a lot!

So, first, the reason for the error is that you are adding a WHERE league_id = condition to your Player CActiveDataProvider, and your Player table does not have a league_id column.
What you should do to get a list of the players in your View is:
1) NOT make a CActiveDataProvider - just pass in the $model to your view
public function actionView($id)
{
$this->render('view',array(
'model'=>$this->loadModel($id),
));
}
2) Then, in your view, just lazy-load your MANY_MANY relation and loop through it like so:
<?php foreach($model->League as $player): ?>
<?php echo $player->name ?>
<?php endforeach; ?>
I would rename your "League" relation "players" or something.
If you really need to use a CActiveDataProvider... I can't remember how to (or if you can) pass in a a relation, but you can do it my manually adding join criteria to your CActiveDataProvider.
I hope this helps! Cheers

try this.
Inside LeagueController
public function actionView($id)
{
$criteria=new CDbCriteria;
$criteria->with='League';
$criteria->together=true;
$criteria->condition='league_id=:leagueId';
$criteria->params=array(':leagueId'=>$this->loadModel($id)->id);
$issueDataProvider = new CActiveDataProvider('Player',array(
'criteria'=>$criteria,
),
'pagination' => array(
'pageSize' => 1,
),
));

Related

how to use relation table when using sqldataprovider

please help I dont know how to get relation table when using sqldataprovider. Anyone understand how to use relation model?
$model = new Finalresult();
$searchModel = new FinalresultSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider = new SqlDataProvider([
'sql' => 'SELECT finalresult.bib,
finalresult.series_id,
finalresult.category_id,
GROUP_CONCAT(DISTINCT finalresult.point ORDER BY series.serie_seri_no DESC) AS seriPoint
FROM finalresult, series GROUP BY finalresult.bib',
'key' => 'bib',
]);
I'm trying to get relation table:
'attribute'=>'category_id',
'width'=>'300px',
'value'=>function ($model, $key, $index, $widget) {
return $model->category->category_name;
},
then getting trying to non-object
You can't use relations with SqlDataProvider, because each single result will be presented as array, for example:
[
'id' => 1,
'name' => 'Some name',
'category_id' => 1,
],
For example, you can access category_id as `$model['category_id'].
SqlDataProvider is for very very complex queries, your query can easily be written as ActiveQuery and you can use ActiveDataProvider and get all advantages of that (relations, etc.).
You can find category by id, but it will be lazily loaded that means amount of queries is multiplied by number of rows.
With ActiveDataProvider and relations you can use eager loading and reduce amount of queries. Read more in official docs.
Grid Columns example in documentation
try to change "value" to
'value'=> function($data) {
return $data['category']['category_name'];
}

cakephp convert custom query to cakephp query

I have the following code in my projects controller's index action
I achieved it via custom queries
1- it is good to use custom queries?
2- how can I write the following queries using cakephp functions joins contains etc
3- which method will be good?
my tables are as below
portfolio_projects [id,name,....,user_id,job_id,manymore_id]
portfolio_images [id,project_id,image,orders,status,.....]
portfolio_tags [id,tag,....]
portfolio_project_tags[id,project_id,tag_id]
and query is as below. I did this to fetch only needed data that is projects with its images and tags but not project's (user,job and others)
there are other tables linked to tags, images tables too but I do not need that data here.
$this->Project->recursive = -1;
$projects = $this->Project->find('all',array(
'conditions' => array(
'Project.user_id' => $this->Auth->user('id')
)
));
foreach($projects as $key=>$project)
{
//fetching project related tags starts here
$q="select * from portfolio_project_tags where 0";
$q="select tag.id as id,tag.tag from portfolio_project_tags as p left outer join portfolio_tags as tag on p.tag_id=tag.id where p.project_id=".$project['Project']['id'];
$tags = $this->Project->query($q);
$projects[$key]['Project']['tags']=$tags;
//fetching project related tags ends here
//fetching project related images starts here
$q2="select * from portfolio_images where 0";
$q2="select img.id as id,img.title as title, img.image as image from portfolio_images as img where img.project_id=".$project['Project']['id']." and img.status='Publish' order by orders";
$snaps = $this->Project->query($q2);
$projects[$key]['Project']['snaps']=$snaps;
//fetching project related images ends here
}
$this->set('projects',$projects);
You have 3 possibilities (at least) to retrieve data from a join table in CakePHP, that you should consider in the following order:
Containable Behaviour
CakePHP join query
Custom query
In your example, only the q1 request need a join because the q2 can be express as:
$this->PorfolioImage->find('all', array(
'fields' => array('PortfolioImage.id, PortfolioImage.title, PortfolioImage.image'),
'conditions' => array(
'PortfolioImage.project_id' => $project['Project']['id']
)
));
The containable behaviour
In your Project model, you could add information to the related models, such as:
class Model extends AppModel {
public $actAs = array('Containable');
public $hasMany = array('PortfolioImage');
public $belongsTo = array('PortfolioTag') ;
}
Then, when you retrieve a Project, you have access to the related PortfolioImage and Tag, for example:
debug($this->Project->read(null, 1)) ;
Would output:
Array(
'Project' => array(
'id' => 1,
/* other fields */
),
'PortfolioTag' => array(
'id' => /* */,
'tag' => /* */
),
'PortfolioImage' => array(
0 => array(
'id' => /* */,
/* other fields */
),
1 => array(/* */)
)
)
You just need to be carefull on the naming of foreign key, the CakePHP documentation about relationship is really easy to read.
CakePHP join query
You can do a join query in CakePHP find method:
$this->Project->find('first', array(
'fields' => array('Tag.id', 'Tag.title'),
'joins' => array(
'table' => 'portfolio_tags,
'alias' => 'PortfolioTag',
'type' => 'LEFT OUTER',
'conditions' => array('PortfolioTag.id = Project.tag_id')
)
)) ;
I'm really not used to CakePHP join queries (the Containable behaviour is often sufficient), but you'll find lot of information in the CakePHP documentation.
Custom queries
You should really not use custom queries except if you really know what you're doing because you have to take query of almost anything (sql injection at first place). At least, when doing custom queries, use Prepared Statements (see information at the end of this link).

CakePHP- querying a HABTM table and then accessing data

CAKEPHP question
I am querying my HABTM table successfully and returning the id of every student with the given group_id. This is my code for this.
$students = $this->GroupsStudents->find('list', array('conditions' => array('group_id' => $id)));
It works, no problem. I need to somehow use this info (namely the student's id), which is stored in $students, to query my students table and extract student's names based on their id.
If someone could give me some insight on this, that would be greatly appreciated.
if i'm understanding you right. as you can see from this if you have the id you can easily get the students name even though i'm not sure why you would do this and not just foreach the name.
foreach ($students as $id => $name) {
echo $students[$id]; // produces name
}
In Student model define relation with GroupStudent model as shown below:
var $hasMany = array(
'GroupStudent' => array(
'className' => 'GroupStudent',
'foreignKey' => 'student_id'
)
);
Then your write your query as
$students = $this->Student->find('all',
array(
'conditions' => array('GroupStudent.group_id' => $id)
'fields'=>array('Student.name','Student.id','GroupStudent.group_id'))
);
Note: Make sure your controller has $uses=>array('Student','GroupStudent'); defined!
and your are using plural names for model GroupStudents so correct them if possible

YII Inventory Control system issue

Guys help me to do this. I'm new to YII. I want to display each item branches stock like this.
actual database
What is the best way to do this?
What you are looking for is a cross tab or pivot table. Here is a link: http://www.artfulsoftware.com/infotree/qrytip.php?id=523
I have been looking for the same thing and have a solution using CSqlDataProvider. It is important to note when using CSqlDataProvider it returns an array unlike CActiveDataProvider which returns an object.
Here is the model code
public function displayStock($id)
{
$sql='SELECT product_id, COUNT(*) as productCount FROM stock WHERE assigned_to = 2 GROUP BY product_id';
$dataProvider=new CSqlDataProvider($sql, array(
'pagination'=>array(
'pageSize'=>10,
),
));
return $dataProvider;
}
Here is the code for the veiw
$stockDataProvider = Stock::model()->displayStock(Yii::app()->user->id);
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'stock-grid',
'ajaxUpdate' => true,
'template'=>'{items}{summary}{pager}',
'dataProvider'=>$stockDataProvider,
'columns'=>array(
'product_id',
array(
'name'=>'test',
'value'=>'$data["productCount"]'
),
array(
'class' => 'CButtonColumn',
'template' => '{view} {update}',
),
),
));
When defining the SQL statement in the model, you can see we have set the COUNT(*) to be called productCount. Then in the view we reference that name in the columns array like so
array(
'name'=>'test',
'value'=>'$data["productCount"]'
),
So simply replace productCount with the name you have used.
I'm sure the above code could do with some tweaking ie using Binded params in the query etc, but this has worked for me.
Any comments or improvements are very welcome. I'm only about 8 months into php and 3 months into yii.

Yii Query optimization MySQL

I am not very good with DB queries. And with Yii it's more complicated, since I am not very used to it.
I need to optimize a simple query
$userCalendar = UserCalendar::model()->findByAttributes(array('user_id'=>$user->id));
$unplannedEvents = CalendarEvent::model()->findAllByAttributes(array('calendar_id'=> $userCalendar->calendar_id,'planned'=>0));
CalendarEvent table, i.e the second table from which I need records does not have an user_id but a calendar_id from which I could get user_id from UserCalendar, i.e. the first table hence I created a UserCalendar object which is not a very good way as far as I understand.
Q1. What could I do to make it into one.
Q2. Yii does this all internally but I want to know what query it built to try it seperately in MySQL(phpMyAdmin), is there a way to do that?
Thanks.
Q1: You need to have the relation between UserCalendar and CalendarEvent defined in both of your active record models (in the method "relations").
Based on your comments, it seems like you have the Calendar model that has CalendarEvent models and UserCalendar models.
Lets assume your relations in Calendar are:
relations() {
return array(
'userCalendar' => array(self::HAS_MANY, 'UserCalendar', 'calendar_id'),
'calendarEvent' => array(self::HAS_MANY, 'CalendarEvent', 'calendar_id'),
}
In CalendarEvent:
relations() {
return array( 'calendar' => array(self::BELONGS_TO, 'Calendar', 'calendar_id'), );
}
And in UserCalendar:
relations() {
return array( 'calendar' => array(self::BELONGS_TO, 'Calendar', 'calendar_id'), );
}
So to make the link between UserCalendar and CalendarEvent you'll need to use Calendar
$criteria = new CDbCriteria;
$criteria->with = array(
"calendarEvent"=>array('condition'=>'planned = 0'),
"userCalendar"=>array('condition'=> 'user_id =' . $user->id),
);
$calendar = Calendar::model()->find($criteria);
and $calendar->calendarEvent will return an array of calendarEvent belonging to the user
Q2: you can enable web logging so all the db request (and others stuffs) will appear at the end of your page:
Logging in Yii (see CWebLogging)
In your application configuration put
'components'=>array(
......
'log'=>array(
'class'=>'CLogRouter',
'routes'=>array(
array(
'class'=>'CWebLogRoute',
),
),
),
),