I'd like to know how (if it's possible) to combine (union) relations in a Yii model.
I have the following models:
User
Country
Province
City
Item
The geographic hierarchy relationship is:
Country has many Provinces
Province has many Cities
City has many Items
Country and Province level has many items: 'through'=>'cities'
The user-geographic relationship is a bit more complex:
User - Country (Many to Many)
User - Province (Many to Many)
User - City (Many to Many)
So a user can be assigned to any Geographic Level and will have implied assignment to all of that level's child levels.
eg. A user assigned to Canada, California and Chicago, will have access to:
All items in all provinces in all cities in Canada
as well as all items in all cities in California
as well as all items in Chicago
As such I would like to see which items are assigned to a user through their Geographic levels. So I would like to set up a relation that will allow me to access all items assigned to them simply by accessing the "items" property (eg. $myUser->items).
Effectively I would like to combine these into a single relation called "items":
'countryItems' => array(self::HAS_MANY, 'Item', array('id' => 'country_id'), 'through'=>'countries'),
'provinceItems' => array(self::HAS_MANY, 'Item', array('id' => 'province_id'), 'through'=>'provinces'),
'cityItems' => array(self::HAS_MANY, 'Item', array('id' => 'city_id'), 'through'=>'cities'),
The problem here is, that you can not create a SQL statement that gets all distinct items of a user for you because your connections between Items and Users are ambiguous: One Item can be connected over different paths to a User. That means, there could even be more than one connection for the same Item to a User (e.g. via a City and via a Province).
You could try to add a JOIN for every possible path between a User and an item (through Country, Province and City). But the mentioned ambiguity will always be in your way: You can end up with a result, that contains duplicates.
And since there's no solution in raw SQL you can not solve it with Yii relations alone. You can always write a getter, though:
public getItems()
{
$items = array();
foreach($this->countryItems as $item)
$items[$item->id] = $item;
foreach($this->provinceItems as $item)
$items[$item->id] = $item;
foreach($this->cityItems as $item)
$items[$item->id] = $item;
return $items;
}
This effectively gives you a $items property that contains all distinct items for that user.
Related
So right now I have four tables
**Users**
user_id;
**Categories**
category_id;
user_id;
**Todos**
todo_id;
**Categories_Todos** (joinTable)
category_id;
todo_id;
so there relationship are
1. A user can have a lot of categories ( one to many)
2. Categories can have a lot of Todos and Todos can also have a lot Categories(many to many)
I accomplish get's certain categories's todo's by following query buillder
const response = await getRepository(TodoItem)
.createQueryBuilder("todoItem")
.leftJoin('todoItem.categories', 'categories')
.where('categories.id=:id',{id:categoryId})
.getMany();
But what I really want to do is to get a user's todos by userID, I think it's like an one to many to many relationship , how can I do that ?
You should also join user table and then get all distinct values:
const response = await getRepository(TodoItem)
.createQueryBuilder("todoItem")
.leftJoin('todoItem.categories', 'categories')
.leftJoin('categories.user', 'user')
.where('user.id=:id',{id: userId})
.distinct(true)
.getMany();
Let's say i have a table of houses, each of which has many rooms, each of which has many items in it. Each item has a color.
Houses
+ id
+ name
Rooms
+ id
+ house_id (FK house.id)
+ name
Items
+ id
+ room_id (FK room.id)
+ name
+ color
The corresponding model relationships ($this->hasMany()) has been set up.
I then want to return a nested JSON object with all the red items in it, with the houses as the top level of the object. That is, the JSON object is structured as a list of houses, each of which has a list of rooms, each of which has a list of items.
To be clear: If a house has no rooms with a red item in it, i don't want the house to be in the JSON object at all. If it only has some rooms with red items in it, i only want those rooms in the object. And finally of course, for the rooms with red items in it, i only want those red items to be in the object.
How can i best do this using constraints like ->where(), or if not possible, how can i best use raw queries to do it?
EDIT:
I already did create a nested query using whereHas, something along the lines of:
House::whereHas('rooms', function ($rooms) {
$rooms->whereHas('items', function ($items) {
$items->where('color', 'red);
});
});
While this only gave me houses that had red items in it, it keeps the non-red items of those houses in the object as well.
To filter out rooms there is not red i believe you can utilize with() the optional query.
House::with(['rooms.items' => function($query) {
$query->where('color', 'red');
}])->whereHas('rooms', function ($rooms) {
$rooms->whereHas('items', function ($items) {
$items->where('color', 'red);
});
});
DB table:
Mcourse(Master course )-> contains Course Names
Lcourse(Linked
Course- courses belongs to a college) -> contains foreign key
Mcourse_Id. & college Id.
Nw the problem is
I want to display list of courses available in a college using dropdownlist.
So sql query is:
select Lcourse_Id, Mcourse_Name* from Lcourse inner join Mcourse on Lcourse_Mcourse_Id=Mcourse Id..
*Id & value pair for dropdownlist
I could do this usin createCommand..Its working pretty fine. But i cant do this usin Relations ..Help me.
Let's imagine for a minute that your Mcourse table is called courses and model for that table is called Courses, your Lcourse table is called courses_colleges and your colleges table is colleges and model for that table is Colleges
Now, You should have Courses model with relations:
public function relations() {
return array(
'colleges' => array(self::MANY_MANY, 'Colleges', 'courses_colleges(course_id, college_id)')
);
}
Your Colleges model should have similar relations:
public function relations() {
return array(
'courses' => array(self::MANY_MANY, 'Courses', 'courses_colleges(college_id, course_id)')
);
}
Now if you want to print out a dropdown with all courses available for a certain college. In your controller action method get the model of that college including its courses:
public function actionShow() {
$id = 1; // We set just some sample id. You could get it from request ofc.
$college = Colleges::model()->with('courses')->findByPk($id);
$this->render('show', array('college'=>$college));
}
Now in your view print out this:
echo CHtml::dropDownList('courses', '', CHtml::listData($college->courses, 'id', 'name'));
Where 'id' and 'name' are columns of your Courses model.
Something like that.
The error is in the listData() function in your view, specifically that you don't have a mc_Id in your Lcourse model.
As you haven't clarified the model that each of those relationships are assigned with, it's impossible to guess what you should substitute for 'mc_Id' in your view - check your Lcourse model to determine the proper column name.
I have a model called Shops with an attribute called brands, brands is a text field and contains multiple brands. What i would like to do is select all unique brands and display them sorted in alphabetic order
#brands = Shop.all(:select => 'distinct(brands)')
What to do from here?
If Shop#brands can hold multiple values like for example: "rony, hoke, fike", then I can reluctantly suggest doing something like this:
#brands = Shop.all(:select => 'brands').each { |s|
s.brands.split(',').map { |b|
b.strip.downcase
}
}.flatten.uniq.sort
BUT, you should really think about your data model here to prevent such hackery. You couuld break out the brands into it's own table + model and do a many to many relationship with Shop.
I have 3 tables, standart relation MANY-TO-MANY
Users(id,...) -> Users_Has_Courses(Users_id, Courses_id) -> Courses(id,...)
Courses Model has next relation
'users' => array(self::MANY_MANY, 'Users', 'users_has_courses(Courses_id, Users_id)')
Users Model has next relation
'courses' => array(self::MANY_MANY, 'Courses', 'users_has_courses(Users_id, Courses_id)')
Please, say how I can get list of courses, on which user with specified "id" hasn't been subscribed with CActiveDataProvider ?
Otherwords, I need an analogue of this plain SQL query
select * from Courses where id not in (select Courses_id from users_has_courses where Users_id = 2)
thanks for the help
Instead of a regular "relation", try a parametrized Named Scope to encapsulate the query. In your Courses model, add this scope function to get a list of all the courses the user is not in:
public function userNotIn($user_id)
{
$criteria=new CDbCriteria();
$criteria->condition .= 't.id NOT IN (SELECT users_has_courses.Courses_id FROM users_has_courses WHERE users_has_courses.Users_id = :userid)';
$criteria->params[':userid'] = $user_id;
$this->getDbCriteria()->mergeWith($criteria);
return $this;
}
Then you should be able to do this:
$coursesNotIn=new CActiveDataProvider(Courses::model()->userNotIn($user->id));
This code is completely untested, but it should work in principle. I do this sort of thing often when I have a complex query but I still want to use the AR features, like CActiveDataProvider. Read more about "named scopes" here:
http://www.yiiframework.com/doc/guide/1.1/en/database.ar#parameterized-named-scopes
Good luck!