Laravel querying data through a pivot table - php

I have a table called 'projects' and a table called 'tags'. These 2 are connected through a pivot table named 'project_tag' so for example like this:
There is 1 record in the projects table with project_id 1
then in the 'project_tag' table there is this :
project_id 1 and tag_id 1
and tag_id 1 is "sometag" for example
Now my problem is I want to query all the projects that has "sometag" as name in the tags table.
Is there a way to do this? Or should I work with the id's instead of the tag values?
What my models look like:
Project model:
public function tags()
{
return $this->belongsToMany('App\Tag')->withTimestamps();
}
Tag model:
public function projects()
{
return $this->belongsToMany('App\Project');
}
I am a bit lost in my database structure :) I am fairly new to laravel
Many thanks in advance!

Assuming your model is called Project and your project-tag relationship is called tags(), this should work out for you:
Project::whereHas('tags', function ($query) {
$query->where('name', 'like', '%sometag%');
})->get();

To query projects by certain tags you ca do like this:
// Get projects by a single tag
$tag = 'mytag';
Project::whereHas('tags', function ($q) use ($tag) {
$q->whereName($tag);
// or $q->whereSlug($tag);
})->get();
// Get projects by multiple tags
$tags = ['mytag', 'othertag', 'iamtag'];
Project::whereHas('tags', function ($q) use ($tags) {
$q->whereIn('name', $tags);
})->get();
Or you can query projects from pivot table like this:
// Get projects by a single tag
$tag_id = 1;
Project::whereIn('id', function($q) use ($tag_id) {
$q->select('project_id')->from('project_tag')->where('tag_id', $tag_id);
})->get();
// Get projects by multiple tags
$tag_ids = [1, 2, 3];
Project::whereIn('id', function($q) use ($tag_ids) {
$q->select('project_id')->from('project_tag')->whereIn('tag_id', $tag_ids);
})->get();

Adding to what #TheFallen has said your models should look like this
class Tag extends Model
{
/**
* Get all of the tags project
*/
public function projects()
{
return $this->belongsToMany('App\Project');
}
}
class Project extends Model
{
/**
* Get all of the project's tag
*/
public function tags()
{
return $this->belongsToMany('App\Tag');
}
}
Then you can do this
Project::whereHas('tags', function ($query) {
$query->where('name', 'like', '%sometag%');
})->get();

Related

How to map using Laravel BelongsTo Relationship

I have a relationship between your_electricity_yesterday_category and building as building_id is present in your_electricity_yesterday_category table.
I am trying to get details out of the building table using the relationship.
I have this in my Electricity model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Electricity extends Model
{
use HasFactory;
protected $connection = 'mysql2';
protected $table = 'your_electricity_yesterday_category';
public function buildings()
{
return $this->belongsTo(Building::class, 'building_id');
}
}
I have this in my Repository
public function getAllBuilding()
{
// $buildings = Building::where('module_electricity', 1)->orderBy('description')->get();
$buildings = Electricity::with('buildings')->get();
return $buildings;
}
I have this in my controller
public function electBuilding()
{
$getBuilding = $this->electricityRepository->getAllBuilding();
return response()->json($getBuilding);
}
On the building table i have a column where module_electricity is either 0 or 1
How can i use this relationship to return building where module_electricity is 1 in json?
use whereHas query builder to filter parent Electricity details based on condition
$buildings = Electricity::with(['buildings'=>function($query){
$query->where('module_electricity',1);
}])
->whereHas('buildings',function($query){
$query->where('module_electricity',1);
})->get();
Also you can write scope for where condition in buildings model like below
public function scopeModuleElectricity($query,$module){
return $query->where('module_electricity',$module);
}
so your query will be
$buildings = Electricity::with(['buildings'=>function($query){
$query->moduleElectricity(1);
}])
->whereHas('buildings',function($query){
$query->moduleElectricity(1);
})->get();
Here is what I came up with:
public function getAllBuilding()
{
return Electricity::query()
->with('buildings', fn ($query) => $query->where('module_electricity', 1))
->get()
->pluck('buildings')
->collapse();
}
Walking through this step by step so you can better understand what's happening:
Initiating a query (completely optional, just for better code formatting)
Eager loading buildings with a condition (module_electricity = 1)
Retrieving data from the database
Extracting buildings only
Flat-mapping results
This will return a single collection with buildings that met a condition.
Let me know if the result turned out to be exactly what you expected.
P.S. Note that the above solution might not work if you're using older versions of PHP. If the above returns syntax error:
replace:
fn ($query) => $query->where('module_electricity', 1)
with:
function ($query) {
$query->where('module_electricity', 1)
}

Laravel withCount() returns wrong value

In my application there are users making pictures of items. The relationship structure is as follows:
Categories -> SubCategories -> Items -> Pictures -> Users
Now, there's also a shortcut relationship called itemPics between categories <--> pictures so that the number of pictures uploaded by a user can be quickly counted per category using withCount().
These are the relationships on the Category model:
public function subcategories()
{
return $this->hasMany('App\SubCategory');
}
public function items()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory');
}
public function itemPics()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory')
->join('pictures','items.id','=','pictures.item_id')
->select('pictures.*');
}
My problem is getting the number of pictures that a user has gathered per category. The itemPics_count column created by withCount() always has the same value as items_count, even though the number of related models for both relations given by with() are different in the JSON output.
$authorPics = Category::with(['SubCategories', 'SubCategories.items' => function ($q) use ($author_id) {
$q->with(['pictures' => function ($q) use ($author_id) {
$q->where('user_id', $author_id);
}]);
}])
->with('itemPics') /* added this to check related models in output */
->withCount(['items','itemPics'])
->get();
dd($authorPics);
This does not make sense to me. Any help is greatly appreciated.
Withcount() function is not work properly if relations include join. it works only table to table relations.
public function itemPics()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory')
->select('pictures.*');
}
This solution was worked for me:
//...
public function itemPics()
{
return $this->hasManyThrough('App\Item', 'App\SubCategory');
}
Then you can do something like this:
$authorPics = Category::with(['SubCategories', 'SubCategories.items' => function ($q) use ($author_id) {
$q->with(['pictures' => function ($q) use ($author_id) {
$q->where('user_id', $author_id);
}]);
}])
->with('itemPics') /* added this to check related models in output */
->withCount(['items','itemPics' => function($query){
$query->join('pictures','items.id','=','pictures.item_id')
->select('pictures.*');
}])
->get();
dd($authorPics);
Link to more information about Laravel withCount function here https://laravel.com/docs/8.x/eloquent-relationships#counting-related-models

Laravel eloquent get all records wherehas all ids in many to many relation

I have a Posts table it has three fields id, title, description.
My Post Model
class Post extends Model
{
use SoftDeletes;
protected $fillable = ['title', 'description'];
public function tags()
{
return $this->belongsToMany(Tag::class, 'post_tag');
}
}
My Tag Model
class Tag extends Model
{
use SoftDeletes;
protected $fillable = ['name'];
public function posts()
{
return $this->belongsToMany(Post::class, 'post_tag');
}
}
Now I want to get posts & paginate where I have a tag filter e.g I have two tags animals & news which has id 1 & 2. Now I want to get all posts which has tag 1 & 2 & paginate. Here is what I tried
Post:: with('tags')->whereHas('tags', function($q) {
$q->whereIn('id', [1, 2]);
})->paginate();
But here as I am whereIn it returns posts has tags 1 or 2 or both. But I want post who has both tag id 1 & 2.
I am using Laravel 5.2.
You'll have to loop through your list of ids to add that condition, then. For instance:
$query = Post::with('tags');
foreach ($ids as $id) {
$query->whereHas('tags', function($q) use ($id) {
$q->where('id', $id);
});
}
$query->paginate();
I have been looking for the same thing and inspired by this stackoverflow MySQL answer, I have ended up with this
Code:
Post:: with('tags')->whereHas('tags', function($q) {
$idList = [1,2];
$q->whereIn('id', $idList)
->havingRaw('COUNT(id) = ?', [count($idList)])
})->paginate();
Because I think I might use it in a few places I have made it into a trait which you can view here. Which if you included the trait in your Post class you could use like the following.
Code:
Post::with('tags')->whereHasRelationIds('tags', [1,2])->paginate();
You can also use whereHas()'s third and fourth parameter in combination with whereIn():
$keys = [1, 2];
$list->whereHas(
'genres',
fn ($query) => $query->whereIn('id', $keys),
'=',
count($keys)
);
I don't think there's a built in method for this, but I would suggest putting the foreach loop inside the whereHas method just for neatness sake.
$query = Post::with('tags')->wherehas('tags', function ($q) use ($ids) {
foreach ($ids as $id) {
$q->where('id', $id);
}
})->paginate(10);

How to get data from foreign key in laravel 5.1?

I am using laravel 5.1 for my new project smart search.
My problem is to get data from foreign key using like query for search of that table.
My database tables are:
category->id, name
search->id, category_id (foreign_key), question, answer, tags
My model code is:
category model
public function helpcenter() {
return $this->belongsTo('App\HelpCenter');
}
helpcenter model
public function category() {
return $this->hasOne('App\HelpCenterCategory', 'id', 'category_id');
}
My controller function for search query is
$queries = HelpCenter::has('category')
->where('questions', 'LIKE', '%'.$term.'%')
->orwhere('category_id.name','LIKE','%'.$term.'%')
->take(5)->get();
You will want to use whereHas() to subquery a relationship:
$queries = HelpCenter::whereHas('category', function($category) use ($term)
{
$category->where('name','LIKE','%'.$term.'%');
})
->orWhere('questions', 'LIKE', '%'.$term.'%')
->take(5)->get();
whereHas() is documented here: http://laravel.com/docs/5.1/eloquent-relationships#querying-relations

Laravel search 'LIKE' query in two tables

I'm currently trying to set up a search bar that filters the results of two tables, books and categories.
I have setup relationships for both models where:
Book.php (Model) (table: id, b_name, b_author, cat_id)
public function bookCategory()
{
return $this->belongsTo('Category', 'cat_id', 'id');
}
Category.php (Model) (table: id, cat_name)
public function book()
{
return $this->hasMany('Book', 'cat_id', 'id');
}
BookController.php
public function getFilterBooks($input)
{
$books = Book::with('bookCategory')->where('**cat_name at category table**. 'LIKE', '%' . $input . '%'')->get();
return Response::json($books);
}
But obviously this won't work. The reason I'm doing this is because I want to allow users to use the same search bar to filter different columns (which I know how to do it in one table, but not two or more).
You can use that.
Book::whereHas('bookCategory', function($q) use ($input)
{
$q->where('cat_name', 'like', '%'.$input.'%');
})->get();
See more in http://laravel.com/docs/4.2/eloquent#querying-relations
EDIT:
Book::with('bookCategory')->whereHas('bookCategory', function($q) use ($input)
{
$q->where('cat_name', 'like', '%'.$input.'%');
})->get();
You get cat_name from relation.

Categories