I have three models Employer , Job, and Transaction
Employer can have many Job
Job can have many Transaction
I am trying to use ActiveRecord to get all Employer that do not have a Transaction record.
Within my Employer model, I have defined relations to find all jobs and transactions linked to this employer:
/**
* #return \yii\db\ActiveQuery
*/
public function getJobs() {
return $this->hasMany(Job::className(), ['employer_id' => 'employer_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getTransactions() {
return $this->hasMany(Transaction::className(), ['job_id' => 'job_id'])->via("jobs");
}
Any ideas on the best way to do this?
SQL:
SELECT employer.*
FROM employer
WHERE employer.employer_id NOT IN
(
SELECT employer.employer_id
FROM employer
INNER JOIN job ON employer.employer_id = job.employer_id
INNER JOIN transaction ON job.job_id = transaction.job_id
)
With Yii2:
function getThoseWithoutTransaction() {
return Employer::find()->where(['not in', 'employer_id',
(new Query())
->select('employer.employer_id')
->from('employer')
->innerJoin('job', 'employer.employer_id = job.employer_id')
->innerJoin('transaction', 'job.job_id = transaction.job_id')
)]
);
}
But I didn't test it. Hope it is correct, though. And there might be better solutions.
Try with
$query = MyModel::find()->where(['not in','attribute',$array]);
Related
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');
I have objects connected mano through the intermediate table.
class City{ /**
* #return \yii\db\ActiveQuery
*/
public function getReviews()
{
return $this->hasMany(Reviews::className(), ['id' => 'review_id'])
->viaTable('city_review', ['city_id' => 'id']);
}
}
And get review for city:
$reviews = $city2->getReviews()->all();
Question:
How select reviews from reviews table, which are not in the table city_review?
I'm not familiar with yii activerecord syntax
But the query you need is below.
SELECT review.* FROM review
LEFT JOIN city_review cr ON cr.city_id = review.id
WHERE cr.id IS NULL
This code should result similar to what you need but you need to test.
$reviews = Reviews::find()
->select('review.*')
->leftJoin('city_review', '`city_review`.`city_id` = `review`.`id`')
->where(['city_review.id' => NULL])
->with('city_reviews')
->all();
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');
I have Many to Many relationship between Institutes and Courses. I want to build query that returns only the institutes list whom some courses has been assigned. I have wrote queries in this situation for one to many. but for not many to many. here is the relationships,
class Institutes {
/**
* #ORM\ManyToMany(targetEntity="Courses", inversedBy="institutes")
* #ORM\JoinTable(name="institute_courses",
* joinColumns={#ORM\JoinColumn(name="institute_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="course_id", referencedColumnName="id")}
* )
*/
protected $courses;
}
class Courses {
/**
* #ORM\ManyToMany(targetEntity="Institutes", mappedBy="courses")
*/
protected $institutes;
}
here is the query that i have written, but didn't work properly.
$repository->createQueryBuilder('s')
->leftJoin('CoursesBundle:Courses','c', 'ON c.institutes = s.courses')
->where('s.active = :active')
->andWhere('s.verified = :active')
->setParameter('active', true)
->orderBy('s.name', 'ASC');
This should do the trick:
$repository->createQueryBuilder('i')
->innerJoin('i.courses','c')
->where('i.active = TRUE')
->andWhere('i.verified = TRUE')
->orderBy('i.name', 'ASC');
You can use a JOIN as you would with other kinds of associations. The following query will find all courses which have been assigned at least to one institute:
SELECT c FROM SomeBundle:Courses c JOIN c.institutes i
You can filter the results further by adding a join condition:
SELECT c FROM SomeBundle:Courses c JOIN c.institutes i WITH i.something = :someParam
I have a Car entity with a many-to-one relationship with an entity Owner. If I select all cars, Doctrine does one query on the Car table, and subsequently one query on the Owner table for each car. So fetching N cars becomes N+1 queries instead of a single JOIN query between the Car and Owner tables.
My entities are as follows:
/** #Entity */
class Car {
/** #Id #Column(type="smallint") */
private $id;
/** #ManyToOne(targetEntity="Owner", fetch="EAGER")
#JoinColumn(name="owner", referencedColumnName="id") */
private $owner;
public function getId() { return $this->id; }
public function getOwner() { return $this->owner; }
}
/** #Entity */
class Owner {
/** #Id #Column(type="smallint") */
private $id;
/** #Column(type="string") */
private $name;
public function getName() { return $this->name; }
}
If I want to list the cars with their owners, I do:
$repo = $em->getRepository('Car');
$cars = $repo->findAll();
foreach($cars as $car)
echo 'Car no. ' . $car->getId() .
' owned by ' . $car->getOwner()->getName() . '\n';
Now this all works very well, apart from the fact that Doctrine issues a query for each car.
SELECT * FROM Car;
SELECT * FROM Owner WHERE id = 1;
SELECT * FROM Owner WHERE id = 2;
SELECT * FROM Owner WHERE id = 3;
....
Of course I'd want my query log to look like this:
SELECT * FROM Car JOIN Owner ON Car.owner = Owner.id;
Whether I have fetch="EAGER" or fetch="LAZY" doesn't matter, and even if I make a custom DQL query with JOIN between the two entities, $car->getOwner() still causes Doctrine to query the database (unless I use EAGER, in which case $repo->findAll() causes all of them).
Am I just too tired here, and this is the way it is supposed to work - or is there a clever way to force Doctrine to do the JOIN query instead?
At least in 1.x Doctrine if you wanted to query for the related objects, you had to use DQL. For your case, the DQL query would look something like this:
//Assuming $em is EntityManager
$query = $em->createQuery('SELECT c, o FROM Car c JOIN c.owner o');
$cars = $query->execute();
Run first a DQL query where you select all the cars joined (DQL JOIN) with the owner. Put the owner in the select().
// preload cars
$qb = $em->createQueryBuilder()
->select('car, owner')
->from('\Entity\Car', 'car')
->leftJoin('c.owner', 'owner');
$query = $qb->getQuery();
// the following seems not needed, but I think it depends on the conf
$query->setFetchMode("\Entity\Car", "owner", "EAGER");
$query->execute(); //you don't have to use this result here, Doctrine will keep it
Doctrine 2 will then perform a JOIN (normally faster as it requires less db queries depending on the number of records).
Now launch your foreach, Doctrine will find the entities internally and it won't run single queries when you need the owner.
Monitor the number of queries first/after each change (eg. mysql general log)
Your query...
$car->getOwner() // "go and fetch this car's owner"
... is in a foreach loop so it will certainly issue the query several times.
If you're writing custom DQL to deal with this, $car->getOwner() shouldn't feature in this at all. This is a function of the Car class. The custom DQL you would write would mimick the exact SQL query you point out and get your join done efficiently.