How to get a related model through a pivot table in Laravel? - mysql

i have a bit of a problem in my Laravel 5.4 application, in my database i have several entities that are related through a central model called Content like such.
Actor -> actors_contents -> Content
Category -> categories_contents -> Content
And then i have three other entities that are "contents" like Video, Photo etc...
Video (content_id) -> Content
Photo (content_id) -> Content
Stream (content_id) -> Content
What i want is to be able to, for instance access all of the Videos for a particular Actor from the Actor model, while also being able to directly get the Actors inside of the Video Model.
So essentially, get the content_id for the current Actor from a pivot table and then find the Videos that match it.
I was trying to use hasManyThrough but after reading for a while i found that it doesn't work with Many-to-many relationships, so my question is how can i make this work otherwise?
I don't want to have to define my own Relationships or anything like that, i could i guess create a method on the Model that simply does some joins to get the values i want, but does that have implications behind the scenes when compared to Laravel's Relationships?
For one
->with(['relation'])
Would no longer work with this relationship and as such i wouldn't be able to eager load it and that might be a bit of a problem, so how would you guys solve this problem?
Thank you in advance for your help.

What I want is to be able to, for instance, access all of the Videos for a particular Actor
One way to do that is to use whereHas():
$actorName = 'John Travolta';
Video::whereHas('content', function ($q) use ($actorName) {
$q->whereHas('actors', function ($q) use ($actorName) {
$q->where('name', $actorName);
});
})->get();

Related

Laravel Get Data From Relation Table

I'm building a project with Laravel 7.28. I have three tables named; projects, tags and project_tags. in project_tags table there is project_ids and tag_ids. It looks like this:
I need to get all projects with their tag and secondly I need to get projects with certain tag. So What should I do in models? Which function and how should I use? And how can I get data?
I discovered rtconner/laravel-tagging package but is it the correct way to do it? Thanks for your help
You might want to create a many to many relationship between projects and tags.
class Project extends Model
{
public function tags()
{
return $this->belongsToMany(Tag::class, 'project_tags')->withTimestamps();
}
}
Then:
// Get all projects with their tags.
Project::with('tags')->get();
// Get projects contain certain a certain tag.
Project::whereHas('tags', function ($query) {
return $query->where('tag', 'some value');
})
In addition, tags tend to be a polymorphic many to many relationships. So if you want to manually handle tagging in the long term, I suggest designing that way.
Also, checkout spatie/laravel-tags package.

Yii2 is there a way to specify tablename in ActiveQuery conditions (like andWhere) in a nice and short way

I make a query (with \yii\db\ActiveQuery) with joins, and some fields in "where" clause become ambigous. Is there a nice and short way to specify the name of the current model`s (ActiveRecord) table (from which one the ActiveQuery was instantiated) before the column name? So I can use this all the time in all cases and to make it short.
Don't like doing smth like this all the time (especially in places where there're no joins, but just to be able to use those methods with joins if it will be needed):
// in the ActiveQuery method initialized from the model with tableName "company"
$this->andWhere(['{{%company}}.`company_id`' => $id]);
To make the "named scopes" to work for some cases with joins..
Also, what does the [[..]] mean in this case, like:
$this->andWhere(['[[company_id]]' => $id]);
Doesn't seem to work like to solve the problem described above.
Thx in advance!
P.S. sorry, don't have enough reputation to create tag yii2-active-query
to get real table name :
Class :
ModelName::getTableSchema()->fullName
Object :
$model::getTableSchema()->fullName
Your problem is a very common one and happens most often with fields liek description, notes and the like.
Solution
Instead of
$this->andWhere(['description'=>$desc]);
you simply write
$this->andWhere(['mytable.description'=>$desc]);
Done! Simply add the table name in front of the field. Both the table name and the field name will be automatically quoted when the raw SQL is created.
Pitfall
The above example solves your problem within query classes. One I struggled over and took me quite some time to solve was a models relations! If you join in other tables during your queries (more than just one) you could also run into this problem because your relation-methods within the model are not qualified.
Example: If you have three tables: student, class, and teacher. Student and teacher probably are in relation with class and both have a FK-field class_id. Now if you go from student via class to teacher ($student->class->teacher). You also get the ambigous-error. The problem here is that you should also qualify your relation definitions within the models!
public function getTeacher()
{
return $this->hasOne(Teacher::className(), ['teacher.id' => 'class.teacher_id']);
}
Proposal
When developing your models and query-classes always fully qualify the fields. You will never ever run into this problem again...that was my experience at least! I actually created my own model-gii-template. So this gets solved automatically now ;)
Hope it helped!

cakephp retrive data from one table excluding the associated tables

I am struggling with a basic problem. i am using cake php 2.5. i try to apply the find query in the company model and receiving all the data from companies and with its associations, but i only want to receive the data from company table and want to exclude the data from rest of relationships, can anyone help me with this. below are my queries.
$this->loadModel('Company');
$fields=array('id','name','logo','status');
$conditions=array('status'=>1);
$search_companies = $this->Company->find('first',
compact(array('conditions'=>$conditions,'fields'=>$fields)));
print_r($search_companies);die();
echo json_encode($search_companies);die();
With out seeing your data output, I am just going to take a stab at the problem.
Inside your $search_companies variable you are getting a multidimensional array probably with the other values of the other tables.
Why not just select the one array:
$wantedData = $search_companies['Company'];
// The key Company (which is the model) should be the data you are wanting.
Try setting model's recursive value to -1
$this->Company->recursive = -1;
$search_companies = $this->Company->find('first',
compact(array('conditions'=>$conditions,'fields'=>$fields)));
With this you will not fire the joins queries and therefore you only retrieve model's information.
Cakephp provide this functionality that we can unblind few/all associations on a any model. the keyword unbindModel is used for this purpose. inside the unblindModel you can define the association type and model(s) name that you want to unblind for that specific association.
$this->CurrentModelName->unbindModel(array('AssociationName' => array('ModelName_Youwwant_unblind')));

Accessing variables outside class

I will try to explain my issue as good as I can.
I have a class with functions, the purpose of one function is to fetch information from the database and display it.
Everything works as it should, but now I need to access some variables to use them outside my class in another file, I don't know how this should be done so I'm wondering if someone can guide me.
function fetch(){
$this->_select_query = '
SELECT movies_id, movies_title, movies_director, movies_year, movies_category_id, cat.name
FROM movies
LEFT JOIN cat ON id = movies_category_id'
or die(mysqli_error());
$this->_stmt = $this->_mysqli->prepare($this->_select_query);
$this->_stmt->execute();
$this->_stmt->bind_result($this->_select_id, $this->_select_title, $this->_select_director, $this->_select_year, $this->_select_category_id, $this->_select_category_name);
$this->_stmt->store_result();
while($this->_stmt->fetch()){
echo '<tr>
<td>'.$this->_select_title.'</td>
<td>'.$this->_select_director.'</td>
<td>'.$this->_select_year.'</td>
<td>'.$this->_select_category_name.'</td>
<td>Edit</td>
<td>Delete</td>
</tr>
';
}
}//close function fetch
The function fetch is inside a class called movies, now on another page I have a form to edit(update) these movies and I would like to return the title, director etc.. inside that form so it is easy to notice what you are changing.
Now I do know how to do this using php procedural but not with object oriented php.
As you can also notice here I echo out the whole table (another part of the table is on a diffirent page)
So because of this I can't use $movies->fetch()
I do hope someone can give me some more information on my issue since I feel a bit lost at this point, and while staring too much on the same code you can become confused and mix up stuff.
Kind Regards
Edit: should I be using globals, constants ?
You should refactor your code.
As you are saying, you can't do $movies->fetch() since it echoes the table. That's a sign of braking the Single Responsibility Principle. You should divide your function into two: one function that fetches (and returns) the data, and another one that takes data as a parameter and returns a table with the data.
That way, you can reuse the first one when you want to get the values for the edit form.
Since you have a procedural background, I'd recommend that you create a new class that represents a "movie" that has the required fields. Then, for each row in your table you should create an instance of that class and populate the fields, and ultimately return all those instances.
Sorry for not posting any code at the moment, I realized that would have been easier.

Can I create sperate queries for different views?

I'm learning sqlalchemy and not sure if I grasp it fully yet(I'm more used to writing queries by hand but I like the idea of abstracting the queries and getting objects). I'm going through the tutorial and trying to apply it to my code and ran into this part when defining a model:
def __repr__(self):
return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
Its useful because I can just search for a username and get only the info about the user that I want but is there a way to either have multiple of these type of views that I can call? or am I using it wrong and should be writing a specific query for getting different data for different views?
Some context to why I'm asking my site has different templates, and most pages will just need the usersname, first/last name but some pages will require things like twitter or Facebook urls(also fields in the model).
First of all, __repr__ is not a view, so if you have a simple model User with defined columns, and you query for a User, all the columns will get loaded from the database, and not only those used in __repr__.
Lets take model Book (from the example refered to later) as a basis:
class Book(Base):
book_id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
summary = Column(String(2000))
excerpt = Column(Text)
photo = Column(Binary)
The first option to skip loading some columns is to use Deferred Column Loading:
class Book(Base):
# ...
excerpt = deferred(Column(Text))
photo = deferred(Column(Binary))
In this case when you execute query session.query(Book).get(1), the photo and excerpt columns will not be loaded until accessed from the code, at which point another query against the database will be executed to load the missing data.
But if you know before you query for the Book that you need the column photo immediately, you can still override the deferred behavior with undefer option: query = session.query(Book).options(undefer('photo')).get(1).
Basically, the suggestion here is to defer all the columns (in your case: except username, password etc) and in each use case (view) override with undefer those you know you need for that particular view. Please also see the group parameter of deferred, so that you can group the attributes by use case (view).
Another way would be to query only some columns, but in this case you are getting the tuple instance instead of the model instance (in your case User), so it is potentially OK for form filling, but not so good for model validation: session.query(Book.id, Book.title).all()