Laravel 5.6 - eager load depending on value in parent table - mysql

Post.php
$fillable = ['id','flag'];
public function tags()
{
return $this->belongsToMany('App\Tags')->withPivot('pivot_flag');
}
public function flaggedTags()
{
return $this->tags()->where('pivot_flag', 1)->get();
}
Tag.php
$fillable = ['id']
public function posts()
{
return $this->belongsToMany('App\Post');
}
Pivot table post_tag columns: post_id,tag_id,pivot_flag
I need to fetch Post with Tags:
$post = Post::select('id','flag')->with('tags')->find($postId);
However, if the flag is set to 1 in Post then I only want tags which have pivot_flag value in pivot table post_tag set to 1
So in that case I'd have following:
$post = Post::select('id','flag')->with('flaggedTags')->find($postId);
How can I do this in single query? I could always do one query to check if the Post is flagged or not and then run the appropriate query for Tags, but that seems wasteful.
Eloquent or raw MySQL queries are welcome.
Thanks!
UPDATE - Alternative solution
Load all tags and then filter the result depending on Post's flag value:
if($post->flag)
{
$tags = $post->tags->filter(funtction($q) {
return $q->pivot->pivot_flag == 1;
});
}
else
{
$tags = $post->tags;
}
This might be faster depending on number of tags. Not really sure, need to test it though. Any feedback about this in comments is welcome.

Related

how to count or get record of rows, whose primary_key is not in foreign table?

I have a database that have two tables 'products' and "specifications". I want to count whose specifications are not in "specifications" table and products have duplicate names.
I have tried: Product::withCount(['specifications'])->has('specifications', '<', '1')->get();
but this is not working, taking too much time also tried with paginate but get no result.
Update 1:
Also please let me know the RAW query for this.
Update 2:
Here is the Product Relation code:
public function specifications()
{
return $this->hasMany('App\Specification');
}
Here is the function I made for deleting duplicate products and there specifications.
public function deleteDuplicateWithCountZero() {
$duplicateProducts = Product::whereIn('name', function ( $query ) {
$query->select('name')->from('products')->groupBy('name')->havingRaw('count(*) > 1');
})->paginate(50);
foreach($duplicateProducts as $duplicateProduct) {
$results = Product::where('name', '=', $duplicateProduct->name)->withCount(['specifications'])->get();
$productCount = count($results);
foreach($results as $result) {
if($result->specifications_count == 0 && $productCount > 1) {
$product = Product::find("$result->id");
Specification::where('product_id', "$product->id")->delete();
$product->delete();
$productCount--;
}
}
}
echo "Complete"; die();
}
Problem: How can I know all products and their specifications are deleted until count query, But count query taking too much time for all data. Please also suggest if I can correct this thing in phpmyadmin with a query.
Since the specification count is always going to be 0, you could hardcode the value in the query. Also, instead of has(), try doesntHave()
Product::query()
->select('*', DB::raw('0 as specifications_count'))
->doesntHave('specifications')
->get();

How to query multiple rows in a column of a table

Below is my controller and when I dd($types_id); I get an array of two ids. Now the problem is when I pass the variable in the where method to pluck the names of the types associated with the ids, it only fetches the name of the first id instead of the two ids. What may I be doing wrong?
/*Get Type List*/
public function getTypeList(Request $request)
{
$types_id = DB::table("vehicles")
->where("condition_id",1)
->pluck("type_id","id")->unique();
//dd($types_id);
$types = DB::table("type_ids")
->where("type_id", $types_id)
->pluck("name");
//dd($types);
return response()->json($types);
}
Problem 1 - You should be using whereIn for the second query.
Problem 2 - Without selecting any column from table and getting all columns for the collection is bad optimization for the fetching operation. You should be using distinct for the query instead of filtering collection by unique method.
public function getTypeList(Request $request)
{
$types_id = DB::table("vehicles")
->where('condition_id',1)
->select("type_id")
->distinct("type_id")->get()
->pluck("type_id");
$types = DB::table("type_ids")
->select('name')
->whereIn("type_id", $types_id)->get()
->pluck("name");
return response()->json($types);
}
Just change where to whereIn
public function getTypeList(Request $request)
{
$types_id = DB::table("vehicles")
->where("condition_id",1)
->pluck("type_id","id")->unique();
//dd($types_id);
$types = DB::table("type_ids")
->whereIn("type_id", $types_id)
->pluck("name");
//dd($types);
return response()->json($types);
}

Eloquent get rows where relation count equals column value

Assuming that i have posts table has field minimum_comments and have query scope in posts table to get the active posts doing something like this
public function scopeActive($query)
{
$query->has('comments', '>=', 'posts.minimum_comments');
}
Putting in consideration that all relation already set up
How can i achieve this?
I haven't test it, but try this:
// Post.php
public function scopeActive($query)
{
return $query->where('minimum_comments', '<=', $this->comments()->count());
}
Then you can go and use it like this:
// PostsController.php
public function myCoolFunction()
{
$posts = Post::active()->get();
// the rest of your logic..
}

Joining Two Tables to a Reference Table Laravel

I Have three tables
#1 Table timeline which is my reference table with an Auto incremented ID which is stored in column id
#2 timeline_videos
#3 timeline_else
What happens is on post if a video is uploaded with the post
it will go into Table #2 ,anything else goes into Table #3.
#2-3 have the Auto Increment Id from the Table timeline stored in a column pid
On query of The Timeline I need to join both tables data using id=pid
so I can use the rest of the Relational Data with the post.
I have done a bit of research and can't seem to find a method for doing so.
So far the code I have
Controller
$groupposts = timeline::where([
['owner','=',$owner],['id','<',$lastid],
])
->join('timeline_videos','timeline.id','=','timeline_videos.pid')
//->join('timeline_else','timeline.id','=','timeline_else.pid')
->orderBy('id','desc')
->limit(5)
->get();
This works with no errors with the second Join commented out but I need to also grab the timeline_else data .
Update --
I have now decided to use Eloquent Relationships to join the tables,
my question now is what type of relationship do I have between the
tables?
I realize it basically needs to be able to switch between two tables to
grab data based on the fact that timeline_videos and timeline_else will not be "JOIN" but separated by type .
The tables need to Join with table #1 timeline based on a column I now have named type for clarifying where to look and matching/joining using the id = pid
You can use relationships.
it sounds like timelines has many videos and has many video failures
https://laravel.com/docs/5.5/eloquent-relationships#one-to-many
you would have a model for each table and set up the relationships
timelines model:
public function videos()
{
return $this-> hasMany('App\Videos');
}
public function videoFailures()
{
return $this-> hasMany('App\videoFailures');
}
videos model:
public function timeline()
{
return $this->belongsTo('App\Timelines');
}
videos failures model:
public function timeline()
{
return $this->belongsTo('App\Timelines');
}
You can then go:
$timeLine = Timmeline::find($id);
to find videos of the time lines you would do:
$videos = $timeLine->videos();
to find else:
$videoElse = $timeLine-> videoFailures();
By using some of what Parker Dell supplied and a bit more trial and error
My Models Looks like
timeline
class timeline extends Model
{
protected $table ='timeline';
public $timestamps = false;
public function videos()
{
return $this->hasMany('App\timeline_videos','pid','id');
}
public function else()
{
return $this->hasMany('App\timeline_ect','pid','id');
}
}
timeline_ect.php ,I changed the name of the table
class timeline_ect extends Model
{
protected $table='timeline_ect';
public $timestamps = false;
public function timeline()
{
return $this->belongsTo('App\Models\timeline','pid','id');
}
}
timeline_videos
class timeline_videos extends Model
{
protected $table='timeline_videos';
public $timestamps = false;
public function timeline()
{
return $this->belongsTo('App\timeline','id','pid');
}
}
Then Lastly my Controller
$timeline = timeline::with('videos','else')
->orderBy('id','desc')
->limit(5)
->get();
So far no Problem query is correct.

Yii JSON with relations

How I can return object with all relations(ans sub objects relations?).
Now I use EJsonBehavior but it returns only first level relations, not sub related objects.
My source code:
$order = Order::model()->findByPk($_GET['id']);
echo $order->toJSON();
Yii::app()->end();
The eager loading approach retrieves the related AR instances together with the main AR instance(s). This is accomplished by using the with() method together with one of the find or findAll methods in AR. For example,
$posts=Post::model()->with('author')->findAll();
The above code will return an array of Post instances. Unlike the lazy approach, the author property in each Post instance is already populated with the related User instance before we access the property. Instead of executing a join query for each post, the eager loading approach brings back all posts together with their authors in a single join query!
We can specify multiple relationship names in the with() method and the eager loading approach will bring them back all in one shot. For example, the following code will bring back posts together with their authors and categories:
$posts=Post::model()->with('author','categories')->findAll();
We can also do nested eager loading. Instead of a list of relationship names, we pass in a hierarchical representation of relationship names to the with() method, like the following,
$posts=Post::model()->with(
'author.profile',
'author.posts',
'categories')->findAll();
The above example will bring back all posts together with their author and categories. It will also bring back each author's profile and posts.
Eager loading may also be executed by specifying the CDbCriteria::with property, like the following:
$criteria=new CDbCriteria;
$criteria->with=array(
'author.profile',
'author.posts',
'categories',
);
$posts=Post::model()->findAll($criteria);
or
$posts=Post::model()->findAll(array(
'with'=>array(
'author.profile',
'author.posts',
'categories',
)
);
I found the solution for that. you can use $row->attributes to create data
$magazines = Magazines::model()->with('articles')->findAll();
$arr = array();
$i = 0;
foreach($magazines as $mag)
{
$arr[$i] = $mag->attributes;
$arr[$i]['articles']=array();
$j=0;
foreach($mag->articles as $article){
$arr[$i]['articles'][$j]=$article->attributes;
$j++;
}
$i++;
}
print CJSON::encode(array(
'code' => 1001,
'magazines' => $arr,
));
This is the best piece of code I found after a long time search to meet this requirement.
This will work like Charm.
protected function renderJson($o) {
//header('Content-type: application/json');
// if it's an array, call getAttributesDeep for each record
if (is_array($o)) {
$data = array();
foreach ($o as $record) {
array_push($data, $this->getAttributes($record));
}
echo CJSON::encode($data);
} else {
// otherwise just do it on the passed-in object
echo CJSON::encode($this->getAttributes($o));
}
// this just prevents any other Yii code from being output
foreach (Yii::app()->log->routes as $route) {
if ($route instanceof CWebLogRoute) {
$route->enabled = false; // disable any weblogroutes
}
}
Yii::app()->end();
}
protected function getAttributes($o) {
// get the attributes and relations
$data = $o->attributes;
$relations = $o->relations();
foreach (array_keys($relations) as $r) {
// for each relation, if it has the data and it isn't nul/
if ($o->hasRelated($r) && $o->getRelated($r) != null) {
// add this to the attributes structure, recursively calling
// this function to get any of the child's relations
$data[$r] = $this->getAttributes($o->getRelated($r));
}
}
return $data;
}