Laravel Eloquent - get count of another table - php

I have an eloquent model "Athlete" and there is another table performances. Each athlete has 0 to many performances. And I would like get best performance of each athlete(personal best) or null if the athlete doesnt have any performances yet.
My athlete model:
class Athlete extends Model
{
// I would like to do something like
public $personalBest = max(performances) - the highest perfomance
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'athletes';
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
/**
* Get the performances for the Athelete post.
*
* #return HasMany
*/
public function performances()
{
return $this->hasMany('App\EloquentModels\Performance', 'athlete_id', "id");
}
}
I would like to get the highest performance of each athlete. Hope it does make sense.
I think it has to be answered somewhere but I had no luck finding it. So sorry if I just failed to find it.
Performances table
id(int) year(int) performance(float)
-------------------------------------
1 2000 257.3
2 2001 227.3
Just to wrap things up. Posting the final raw query which was generated:
select [athletes].[first_name], [athletes].[last_name], MAX(performance) AS personal_best
from [athletes]
left join [performances] on [athletes].[id] = [performances].[athlete_id]
group by [athletes].[id], [athletes].[first_name], [athletes].[last_name]
order by [personal_best] desc

Using withCount should do the job
$athletes= App\Athlete::withCount('performances')->get();
foreach ($athletes as $athlete) {
echo $athlete->performances_count;
}
If you want max performance, you can do something like
$athletes= App\Athlete::all();
foreach ($athletes as $athlete) {
echo $athlete->performances->pluck('performance')->max();
}
Something like
select e.athelete.id, max(perf.performace) as max_performace
from atheletes ath
left join performaces perf on ath.id = perf.athelete_id
group by ath.id, max_performace
may be something like
DB('athletes as ath')::leftJoin('performaces as perf', 'ath.id', 'perf.athelete_id')->select(e.athelete.id, max(perf.performace) as max_performace)->orderBy('max_performace');
You can use order by max_performace, if you need.
I think you can also use simply
echo $athlete->performances->max('performance');

Related

Eloquent hasMany Not Returning Any Results

I have a noob question about how Eloquent generates SQL for the following.
DB:
Customer Table:
ID (Public Key)
... (Some general columns)
Group Table:
ID (Public Key)
FK_CUSTOMER_ID (Foreign Key)
... (Some general columns)
I have the following code in Customer Model:
public function groups()
{
return $this->hasMany(Group::class, 'fk_customer_id');
}
I am trying to get all groups (in the groups function above), that I can narrow down the groups later, to a particular customer.
The above code generates the following SQL (which results an empty result set, which is understandable by looking at the SQL). I have no idea, why the where clause (see the SQL below) gets generated, does not makes much sense.
select * from `group` where `group`.`fk_customer_id` is null and `group`.`fk_customer_id` is not null limit 1
I would like the following SQL to be generated :
select * from `group`
also, how to get the following SQL generated (If I need to select groups based on customer_id, I believe I'll need to add some where clause somehow)
select * from `group` where `group`.`fk_customer_id`= SOME_VALUE
Thanks!
--- Customer Model
<?php
namespace App;
use App\Role;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
/**
* Get ALL groups for the customer.
*/
public function groups()
{
return $this->hasMany(Group::class, 'fk_customer_id');
}
/**
* Get ONE group for the customer.
*/
public function group($groupId)
{
return $this->hasMany(Group::class, 'fk_customer_id')->where('id', $groupId);
}
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'customer';
}
Group Model
<?php
namespace App;
use App\Group;
use App\Customer;
use Illuminate\Database\Eloquent\Model;
class Group extends Model
{
/**
* Get ONE customer for the group.
*/
public function customer()
{
return $this->belongsTo(Customer::class, 'fk_customer_id');
}
/**
* Get ONE directory configuration for the group.
*/
public function directoryConfiguration()
{
return $this->belongsTo(DirectoryConfiguration::class, 'fk_directory_configuration_id');
}
/**
* Get ONE user for the group.
*/
public function user($userId)
{
return $this->hasMany(User::class, 'fk_role_id')->where('user_id', $userId);
}
/**
* Get ALL user for the group.
*/
public function users()
{
return $this->hasMany(User::class, 'fk_role_id');
}
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'group';
}

Need to 'convert' an MySQL-query to Eloquent; where to start / how to think?

I'd like to 'convert' a raw SQL-query to Eloquent, so I can have eager loaded models attached too, so I don't have to edit some templates I got. Problem is, the query got some subqueries and I do not know how to 'convert' the query into Eloquent's format. The query in question is:
SELECT
e_eh.id,
s.name as serie,
s.id as serie_id,
e_eh.season,
e_eh.episode,
e_eh.name,
eh1.prog_trans,
eh1.prog_check,
eh1.prog_sync,
eh1.avi
FROM (
SELECT
e.*
, (
SELECT
eh.id
FROM episode_histories AS eh
WHERE 1
AND eh.episode_id = e.id
ORDER BY
eh.id DESC
LIMIT 1
) AS eh_id
FROM episodes AS e
WHERE 1
AND e.completed = 0
AND e.deleted_at IS NULL
) AS e_eh
INNER JOIN episode_histories AS eh1 ON e_eh.eh_id = eh1.id
INNER JOIN series as s ON s.id = e_eh.serie_id
ORDER BY prog_trans DESC, prog_check DESC, prog_sync DESC
I've tried a few things already, but none have worked. I'm a bit stuck in how to "think" this into Laravel / Eloquent. Documentation from Laravel itself is also not much helpful.
In a nutshell:
I've got two models, one is episodes, other is episode_histories, whichs stores some history on related episode. A third model is the show model, the related show for it. I need to get an episode, with related show model (is a relation in my model already). but I also need to get the latest episode_histories model for given episode.
What I currently have in my models:
Episode:
`class Episode extends Model
{
use SoftDeletes;
use App\History; // The history model
protected $table = 'episodes';
protected $primaryKey = 'id';
public $timestamps = true;
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = ['deleted_at'];
/* Eloquent relations */
public function show() {
return $this->belongsTo('App\Serie', 'serie_id', 'id');
}
public function history() {
return $this->hasMany('App\History', 'episode_id', 'id')->orderBy('id', 'desc');
}
public static function getEpisodes2() {
return DB::select();
}
}
And my history model looks like this:
class History extends Model
{
use SoftDeletes;
protected $table = 'episode_histories';
protected $primaryKey = 'id';
public $timestamps = true;
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = ['deleted_at'];
/* Eloquent relations */
public function episode() {
return $this->belongsTo('App\Episode');
}
public function user() {
return $this->belongsTo('App\User', 'user_id', 'id');
}
/* Custom functions */
}`
I hope someone can help me out on this. In the event of missing info, please let me know, so I can add that.
If you want to do this query types I would recommend you to use the Laravel Query Builder. Take a look at the documentation
The best advice when for dealing with complex queries is to keep it raw. Other than that, you have models for the helping hand because when a change comes to this query, there is a lot of editing and reshuffling again to blend it to a perfect state.
DB:statement("your query")
If you have procedures, then:
DB::statement('CALL PROCEDURE_NAME(:id,#name,#email);',array($id);
$result = DB:select('select #name as alias_name, #email as alias_name');

How does inner join work on a many-to-many relationship using Doctrine and Symfony2

I recently worked out an issue with querying ManyToMany relationship join tables, the solution was same as this answer and was wondering how it works.
lets say i have a simple ManyToMany relationship between groups and team, there will be a groups_team tables that will automatically be created here
groups entity
/**
* Groups
*
* #ORM\Table(name="groups")
* #ORM\Entity(repositoryClass="AppBundle\Model\Repository\GroupsRepository")
*/
class Groups {
/**
* #ORM\ManyToMany(targetEntity="Team", inversedBy="group")
*/
protected $team;
public function __construct() {
$this->team = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="groupname", type="string", length=255)
*/
private $groupname;
//obligatory getters and setters :)
team entity
/**
* Team
*
* #ORM\Table(name="team")
* #ORM\Entity(repositoryClass="AppBundle\Model\Repository\TeamRepository")
*/
class Team {
/**
* #ORM\ManyToMany(targetEntity="Groups", mappedBy="team")
*/
protected $group;
public function __construct(){
$this->group = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="teamname", type="string", length=255)
*/
private $team;
//[setters and getters here]
in order to get all the teams in a group i would have to query the groups_team table.i would have directly queried the table in just mysql but in symfony i have to do this
$groups = $em->getRepository("AppBundle\Model\Entity\Groups")->findBy(array('tournament' => $tournament->getId()));
//get all teams with group id in groups_team table
foreach ($groups as $group) {
$teamsingroup = $em->getRepository("AppBundle\Model\Entity\Team")->createQueryBuilder('o')
->innerJoin('o.group', 't')
->where('t.id = :group_id')
->setParameter('group_id', $group->getId())
->getQuery()->getResult();
echo "</b>".$group->getGroupname()."</b></br>";
foreach ($teamsingroup as $teamingroup) {
echo $teamingroup->getTeam()."</br>";
}
}
Can someone explain to me how the innerJoin is working and what is the concept behind this, maybe a few documentation to learn about this. are there better way to do this with symfony and doctrine.
Using ManyToMany between 2 entities involves a third table generally called as a junction table in this type of relation when you build a DQL (doctrine query) doctrine automatically joins junction table depending on the nature of relation you have defined as annotation so considering your query
$teamsingroup = $em->getRepository("AppBundle\Model\Entity\Team")
->createQueryBuilder('o')
->innerJoin('o.group', 't')
You are joining Team entity with Group entity in innerJoin('o.group') part o is the alias for Team entity and o.group refers to property defined in Team entity named as group.
/**
* #ORM\ManyToMany(targetEntity="Groups", mappedBy="team")
*/
protected $group;
Which has a ManyToMany annotation defined for this type of relation doctrine joins your team table first with junction table and then joins your junction table with groups table and the resultant SQL will be something like
SELECT t.*
FROM teams t
INNER JOIN junction_table jt ON(t.id = jt.team_id)
INNER JOIN groups g ON(g.id = jt.group_id)
WHERE g.id = #group_id
Another thing related your way of getting team for each group you can minimize your code by excluding createQueryBuilder part within loop, once you have defined teams property as ArrayCollection i.e $this->team = new ArrayCollection(); on each group object you will get collections of teams associated to that particular group by calling getTeam() function on group object similar to below code.
foreach ($groups as $group) {
$teamsingroup = $group->getTeam();
echo "</b>".$group->getGroupname()."</b></br>";
foreach ($teamsingroup as $teamingroup) {
echo $teamingroup->getTeam()."</br>";
}
}
I guess it's literally select statement with INNER JOIN using key columns defined entity class as mappedBy or inversedBy.
Why don't you have a look of doctrine log and see what the native sql is composed?
How to get Doctrine to log queries in Symfony2 (stackoverflow)
http://vvv.tobiassjosten.net/symfony/logging-doctrine-queries-in-symfony2/ (some code examples)
I don't know your user story behind this, but I also heard that it is recommended to use one to many relationship instead of many to many, unless there is a strong reason to do so, as most of cases can be handled by one to many by reconsidering models.

DQL for selecting a row in a x-to-x relationship

I have 3 different entities that are mapped to each other. To give a very simplified example.
ProductList:
class ProductList
{
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="productlist") */
protected $products;
}
Product:
class Product
{
/**
* #ORM\ManyToOne(targetEntity="Edition")
* #ORM\JoinColumn(name="edition_id", referencedColumnName="id")
*/
protected $edition;
/**
* #ORM\ManyToOne(targetEntity="ProductList")
* #ORM\JoinColumn(name="productlist_id", referencedColumnName="id")
*/
protected $productlist;
}
Edition
class Edition
{
protected $id;
}
How can I get all the productLists in my productListRepository that contain only 1 product with an specific edition id using DQL?
I know how to do it the 'lazy' way by retrieving all the items from the productList and check whether the related product entity contains an specific edition id but this seems highly ineffective if there is a large dataset to iterate through.
I know I have to use a join but I am stuck at the idea that I need to iterate through the products in productLists. ProductLists can contain more then one products but I only need the ones with only a single product and check wether they have an edition with the specific id.
Try this for extract all the productlist for a specified version that have only one edition:
SELECT pl
FROM ProductList pl
-- LEFT JOIN pl.products p
LEFT JOIN p.edition e
WHERE (sum (p1) from Product p1 where p1.productlist = pl.id) = 1
Hope this help

Yii-Active Record return average result of a specific column as a string

I have a quick question. How do I use the MySQL AVG() query with Yii active record model and pass is as a string? This is how I query with my code now but it return to me as a NULL array...
/**
* Gets topic average rating by comments
* #param int $topic_id the topic unique id
*
* #return int rate
*/
public static function WS_countAverageRating($topic_id){
return ExploreComment::model()->findAll(array('select'=>"AVG(rating)",'condition'=>"topic_id='".$topic_id."'"));
}
I think the most elegant way is to do it with a statistical relationship.
Add something like this to your relations inside the model:
'avarageRating' => array(SELF::STAT, 'ExploreComment', 'topic_id', 'select' => 'AVG(rating)'),
Read more about statistical relations here: http://www.yiiframework.com/doc/guide/1.1/en/database.arr#statistical-query

Categories