I've started Yii a month ago and am finding it very intuitive, yet somehow confusing regarding widgets.
In the app i'm developing, although I sometimes use Active Record, I cannot use it's relations as I am using MyIsam (and this cannot be changed) and it has no support for foreign keys.
My issue is I have a CGridView and want to add custom data to it, but am having issues.
It is a 1-to-many relationship with the FK in the right model, but as I said, I cannot use AR's magic.
I have this, an Applications Model and a Profile model. The Profile model has an application FK.
I got a function so when I'm rendering the CGrid I can fetch the name of each Application instead of its *id_app*.
public function appName($id){
$app= Yii::app()->db->createCommand()
->select('name')
->from('tbl_aplications a')
->where('a.id=:id_app', array(':id_app'=>$id))
->queryRow();
return $app;
}
In the auto-generated templates, in the Profile Admin.php view, I got:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'profile-application-grid',
'dataProvider'=>$model->search(), //maybe the issue is with this criteria? It is currently 'as-is' after the template generation
'filter'=>$model,
'columns'=>array(
'id',
'name',
array(
'name'=>'id_app',
'header'=>'Aplication',
And here is my issue, I've tried (and all sorts of variations):
'value' => 'ProfileApplication::model()->appName($data->id_app)',
'value' => 'ProfileApplication::model()->appName(id_app)',
'value' => 'ProfileApplication::model()->appName("id_app")',
and all I get is null as a result because it is passing the actual string instead of each row's value. If I pass a direct ID to the function's query, it returns correct value, like ->where('a.id=:id_app', array(':id_app'=>3))
Is it the search criteria that needs to be altered?
I've found similar questions but all of them use AR such as Profile->application or something along those lines and I as I said, I cannot use it due to MyIsam restrictions.
Any tips are appreciated to this newcomer, or links to a solution regarding a similar issue .
To use the value attribute such as that you need, as PrplHaz4 said, a data provider. Then, the $data variable is what has the magic, and it must be used in a string because it is eval()'ed behind the scenes. Here is an example of what you are trying to do:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'item-grid',
'dataProvider'=>$model->search(),
'columns'=>array(
array(
'name' => 'id',
'header' => 'Item ID',
),
array(
'name' => 'category_search',
'header' => 'Category',
'value' => '$data->category->name',
),
),
)); ?>
That grabs the name attribute of the related item category model. In the relations() function of the item model:
return array(
'category'=>array(self::BELONGS_TO, 'ItemCategory', 'category_id'),
);
And in the relations() function of the item category model:
return array(
'items'=>array(self::HAS_MANY, 'Item', 'category_id'),
);
You should still be able to use ActiveRecord relations with MyISAM, I beleive the only difference is that with MyISAM, if you use a model generator (gii or cmd line), it will not automatically create the relations. Instead, you will have to specify the relation yourself in the Profile model. This is effectively creating a "soft" fk for use with AR.
public function relations()
{
return array(
'applications'=>array(self::HAS_MANY, 'Applications', 'id_app'),
);
}
That will not entirely solve your problem though, because you will need to use a dataprovider that brings the application models along with the profile models. Something like this:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'profile-application-grid',
'dataProvider'=>Profile::model()->with('Applications'),
'filter'=>$model,
'columns'=>array(
'id',
'name',
array(
'name'=>'applications.name',
'header'=>'Application',
),
Related
Hello everyone and thanks for reading , i have little problem , i installed the yii user extension.
I created another table that has a relationship with the user table.
Yii created the relationships with the "user" table automatically but when i try to use the relationship it gives me the below message;
include(Users.php): failed to open stream: No such file or directory
Could this be because User Model is not with the other models but instead in Modules/User.....
How can i make it work ?
eg
array(
'header' => __('Title'),
'name' => 'id_employer_contract',
'value' => '$data->user->username',//user is the name of relationship
),
When trying to access the User.php model from outside of the User module, the file has not been imported yet.
If you look at the init() method of the UserModule.php file, you'll see that the User.php file gets imported via the user.models.* statement.
There are a few different way to import this file from other parts of your system:
Add application.modules.user.models.* to the import section of your main.php. This will make it available everywhere in your application
Call Yii::import('application.modules.user.models.*'); right before the area of code depending on User.php
Call Yii::app()->getModule('user'); right before the area of code depending on User.php. This will call the init() method in the User module.
Look at "include(Users.php): failed"
You can change the relation mapping (auto generated by Gii) from this:
'user' => array(self::BELONGS_TO, 'Users', 'user_id')
To this:
'user' => array(self::BELONGS_TO, 'User', 'user_id')
That should do the trick.
In another version of Yii the according model might be YumUser instead of User when creating your relation, did the job for me:
'user' => array(self::BELONGS_TO, 'YumUser', 'user_id')
The problem that you have is that the Table name for users is called "users" so when yii is resolving this for model generation it would resolve the user model to be "Users" however the yii-user module extension has the model as "UserModel" in a file named User.php
This means that in essesnce the file named Users.php does not exist.
To resolve this just change the relationship in your new model to'user' => array(self::BELONGS_TO, 'User', 'User'), and not 'user' => array(self::BELONGS_TO, 'Users', 'User'),
Sorry ... my english is bad, so my question could be absolutely not clear.
I have this tables:
Groups
Users
Reports
Relations:
Groups 1 -> N Users 1 -> N Reports
In the admin view of report I'm using a CGridView.
i'm actually able to show user name. ... but what about show group.name ?
How to setup relations ?
How to setup CDbCriteria ?
I already have this relation In Report model:
'user' => array(self::BELONGS_TO, 'Users', 'userId'),
And this in User model
'group' => array(self::BELONGS_TO, 'Gruppi', 'groupId'),
In Report Model I have this:
$criteria=new CDbCriteria;
$criteria->with = array ('user');
You should be able to get the group.name like this(from report's admin view):
user.group.name
And your current relations will do, also you do not have to use any criteria.
You can also use like this:-
$criteria->with = array('user', 'user.group');
group.name
One more solution for the problem. (Tested)
Does anyone know how to do separate login forms and authenticate on two different tables?
I can't go with one table and different roles... client requested to do separate tables...
I've got user login system based on Yii framework login system. But now I have to do separate for admin user (administration module).
The way I solved this issue was to create two identical copies of this plugin from the Yii Framework library:
http://www.yiiframework.com/extension/yii-user/
Then I refactored it and called it "Customer" and changed the config so that it used a different table etc.
In the configuration options for Yii, I also included these options to keep the sessions separate (config/main.php):
'components' => array(
...
'user' => array(
// enable cookie-based authentication
'allowAutoLogin' => true,
'loginUrl' => array('/user/login'),
'class' => 'RWebUser', // added - possibly uses the Rights user manager
),
'customer' => array(
// enable cookie-based authentication
'allowAutoLogin' => true,
'loginUrl' => array('/customer/login'),
'stateKeyPrefix' => 'customer',
),
'customerUser' => array(
'class' => 'CWebUser',
'stateKeyPrefix' => 'customer',
'loginUrl' => array('/customer/login'),
),
You could add a property to your UserIdentity component called, for example, role. Then change the authenticate() method of UserIdentity so that it fetches from the account table corresponding to role. Now you need to make sure that UserIdentity->role is set before invoking UserIdentity->authenticate(). If you are following the yiic webapp template then this would be in SiteController. Two very easy ways (among others):
Have two different login pages, one for normal users and one for admins and each has its own URL. Implement it with two views and two login action methods in SiteController, each sets up UserIdentity->role appropriately before invoking UserIdentity->authenticate(). This approach duplicates code and you'll be able to see how to sort that out once it's working.
Use one login page with a form element (checkbox perhaps) that an admin user selects. This form's action method UserIdentity->role according to form state.
My code is still just in development, not production, and I'm hitting a wall with generating data that I want for some views.
Without burying you guys in details, I basically want to navigate through multiple model associations to get some information at each level. The one association giving me problems is a polymorphic belongs_to. Here are the most relevant associations
Model Post
belongs_to :subtopic
has_many :flags, :as => :flaggable
Model Subtopic
has_many :flags, :as => :flaggable
Model Flag
belongs_to :flaggable, :polymorphic => true
I'd like to display multiple flags in a Flags#index view. There's information from other models that I want to display, as well, but I'm leaving out the specifics here to keep this simpler.
In my Flags_controller#index action, I'm currently using #flags = paginate_by_sql to pull everything I want from the database. I can successfully get the data, but I can't get the associated model objects eager-loaded (though the data I want is all in memory). I'm looking at a few options now:
rewrite my views to work on the SQL data in the #flags object. This should work and will prevent the 5-6 association-model-SQL queries per row on the index page, but will look very hackish. I'd like to avoid this if possible
simplify my views and create additional pages for the more detailed information, to be loaded only when viewing one individual flag
change the model hierarchy/definitions away from polymorphic associations to inheritance. Effectively make a module or class FlaggableObject that would be the parent of both Subtopic and Post.
I'm leaning towards the third option, but I'm not certain that I'll be able to cleanly pull all the information I want using Rails' ActiveRecord helpers only.
I would like insight on whether this would work and, more importantly, if you you have a better solution
EDIT: Some nit-picky include behavior I've encountered
#flags = Flag.find(:all,:conditions=> "flaggable_type = 'Post'", :include => [{:flaggable=>[:user,{:subtopic=>:category}]},:user]).paginate(:page => 1)
=> (valid response)
#flags = Flag.find(:all,:conditions=> ["flaggable_type = 'Post' AND
post.subtopic.category_id IN ?", [2,3,4,5]], :include => [{:flaggable=>
[:user, {:subtopic=>:category}]},:user]).paginate(:page => 1)
=> ActiveRecord::EagerLoadPolymorphicError: Can not eagerly load the polymorphic association :flaggable
Don't drop the polymorphic association. Use includes(:association_name) to eager-load the associated objects. paginate_by_sql won't work, but paginate will.
#flags = Flag.includes(:flaggable).paginate(:page => 1)
It will do exactly what you want, using one query from each table.
See A Guide to Active Record Associations. You may see older examples using the :include option, but the includes method is the new interface in Rails 3.0 and 3.1.
Update from original poster:
If you're getting this error: Can not eagerly load the polymorphic association :flaggable, try something like the following:
Flag.where("flaggable_type = 'Post'").includes([{:flaggable=>[:user, {:subtopic=>:category}]}, :user]).paginate(:page => 1)
See comments for more details.
Issues: Count over a polymorphic association.
#flags = Flag.find(:all,:conditions => ["flaggable_type = 'Post' AND post.subtopic.category_id IN ?",
[2,3,4,5]], :include => [{:flaggable => [:user, {:subtopic=>:category}]},:user])
.paginate(:page => 1)
Try like the following:
#flags = Flag.find(:all,:conditions => ["flaggable_type = 'Post' AND post.subtopic.category_id IN ?",
[2,3,4,5]], :include => [{:flaggable => [:user, {:subtopic=>:category}]},:user])
.paginate(:page => 1, :total_entries => Flag.count(:conditions =>
["flaggable_type = 'Post' AND post.subtopic.category_id IN ?", [2,3,4,5]]))
To make relationship between users was created a table that looks like.
sql
CREATE TABLE `friends`(
`from` INT NOT NULL,
`to` INT NOT NULL,
UNIQUE INDEX(`from`, `to`)
);
As you may know - field from and to is a keys of user_id from users table.
I'm using Kohana 3.09 with its default module Auth.
Question is...
*How to make ORM functionality around relations of users with (default) Model_User class?*
Is there any needle to create additional class or perhaps i had some mistakes with relations one_to_many trouth and many_to_many trouth cause it did not work.
Please help.
My best regards.
You should check out this section of the documentation:
http://kohanaframework.org/guide/orm/relationships#hasmany
You'll need something like this within your user class
protected $_has_many = array(
'friends' => array(
'model' => 'user',
'through' => 'friends',
'far_key' => 'from',
'foreign_key' => 'to',
)
);
These options are used at this part in the source code.