Parent, Child, Child of Child in Laravel - laravel

I have three models, Province, City and Job.
Province has the following:
public function cities() {
return $this->hasmany('City');
}
City has the following:
public function province() {
return $this->belongsTo('Province', 'province_id');
}
public function jobs() {
return $this->hasmany('Job');
}
Job has the following:
public function city() {
return $this->belongsTo('City', 'city_id');
}
I am trying to get the total number of jobs in each province and the following doesn't work. Would appreciate if someone could point out what I am doing wrong?
$province->cities->jobs->count()
Thanks!

You have ready-to-use solution in Eloquent, no need for loading everything and adding up count on the collections.
// Province
public function jobs()
{
return $this->hasManyThrough('Job', 'City');
}
then just:
$province->jobs()->count();
It runs simple SELECT count(*) without loading redundant collections.
Additionally, if you need to eager load that count on the collection of provinces, then use this:
public function jobsCount()
{
return $this->jobs()->selectRaw('count(*) as aggregate')->groupBy('province_id');
}
public function getJobsCountAttribute()
{
if ( ! array_key_exists('jobsCount', $this->relations)) $this->load('jobsCount');
return $this->getRelation('jobsCount')->first()->aggregate;
}
With this you can easily get the count for multiple provinces at once (with just 2 queries executed):
$provinces = Province::with('jobsCount')->get();
foreach ($provinces as $province)
{
$province->jobsCount;
}

This is because cities is a Collection you have to loop through each of them to get the jobs number and add them up.
Within controller: Like so;
$job_count = 0;
$province->cities->each(function ($city) use ($job_count){
$job_count += $city->jobs->count();
});
The $job_count would be equal to the total number of jobs within each of it cities.
Please Note: Be sure to eager load your relations data to reduce the amount of queries that are made on your database.
$province = Province::with('cities', 'cities.jobs')...

Related

Laravel 5.7 many to many sync() not working

I have a intermediary table in which I want to save sbj_type_id and difficulty_level_id so I have setup this:
$difficulty_level = DifficultyLevel::find(5);
if ($difficulty_level->sbj_types()->sync($request->hard, false)) {
dd('ok');
}
else {
dd('not ok');
}
Here is my DifficultyLevel.php:
public function sbj_types() {
return $this->belongsToMany('App\SbjType');
}
and here is my SbjType.php:
public function difficulty_levels() {
return $this->hasMany('App\DifficultyLevel');
}
In the above code I have dd('ok') it's returning ok but the database table is empty.
Try to change
return $this->hasMany('App\DifficultyLevel');
to
return $this->belongsToMany('App\DifficultyLevel');
The sync() method takes an array with the id's of the records you want to sync as argument to which you can optionally add intermediate table values. While sync($request->hard, false) doesn't seem to throw an exception in your case, I don't see how this would work.
Try for example:
$difficulty_level->sbj_types()->sync([1,2,3]);
where 1,2,3 are the id's of the sbj_types.
You can read more about syncing here.

How to call information from one model to another Codeigniter

I'm stuck on this from a while.Can't figured it out.I reed documantion, tried with several videos and tried like 10 different ways, nothing is working yet.So I have one view/model for one thing, in this example Destination and I have separate files for Offers.The controllers for both are in one file.I want tho the information that is in destination to go to Offers as well.Please help I can't figure out what I'm missing:
So here is the most important parts:
destination_model.php
<?php class Destination_model extends CI_Model
{
public function getDestinationDetails($slug) {
$this->db->select('
id,
title,
information
');
$this->db->where('slug', $slug);
$query = $this->db->get('destinations')->row();
if(count($query) > 0)
{
return $query;
}
else
{
// redirect(base_url());
}
}
public function getOffersByDestination($destination_id)
{
$this->db->select('
o.short_title,
o.price,
o.currency,
o.information,
o.long_title_slug,
oi.image,
c.slug as parent_category
');
$this->db->from('offers o');
$this->db->join('offers_images oi', 'oi.offer_id = o.id', 'left');
$this->db->join('categories c', 'c.id = o.category');
$this->db->group_by('o.id');
$this->db->where('o.destination', $destination_id);
$this->db->where('o.active', '1');
return $this->db->get();
} }
And then in the controller for offers I put this:
$this->load->model('frontend/destination_model');
$this->params['destination'] = $this->destination_model->getOffersByDestination($data->id);
All I need is the title and the information about the destination.
Here is the whole controller for the offers:
$data = $this->slugs_model->getOfferDetails(strtok($this->uri->segment(2), "."));
$this->load->model('frontend/offers_model');
$this->load->model('frontend/destination_model');
$this->params['main'] = 'frontend/pages/offer_details';
$this->params['title'] = $data->long_title;
$this->params['breadcumb'] = $this->slugs_model->getSlugName($this->uri->segment(1));
$this->params['data'] = $data;
$this->params['images'] = $this->slugs_model->getOfferImages($data->id);
$this->params['similar'] = $this->slugs_model->getSimilarOffers($data->category, $data->id);
$this->params['destination'] = $this->destination_model->getOffersByDestination($data->id);
$this->params['offers'] = $this->offers_model->getImportantOffers($data->offers, $data->category, $data->id);
You need to generate query results after you get it from model,
e.g: row_array(), this function returns a single result row.
here's the doc: Generating Query Results
try this:
$this->load->model('frontend/destination_model');
$data['destination'] = $this->destination_model->getOffersByDestination($data->id)->row_array();
$this->load->view('view_name', $data);
And in your view echo $destination['attribut_name'];,
or you can print the array, to see if it's work print_r($destination);

Querying counts from large datasets using eloquent

I have the following relationships:
A Job has many Roles.
public function roles()
{
return $this->hasMany(Role::class);
}
A Role has many Shifts and Assignments through shifts.
public function shifts()
{
return $this->hasMany(Shift::class);
}
public function assignments()
{
return $this->hasManyThrough(Assignment::class, Shift::class);
}
A Shift has many Assignments.
public function assignments()
{
return $this->hasMany(Assignment::class);
}
I need to get a count of all assignments with a certain status, let's say "Approved". These counts are causing my application to go extremely slowly. Here is how I have been doing it:
foreach ($job->roles as $role){
foreach ($role->shifts as $shift) {
$pendingCount = $shift->assignments()->whereStatus("Pending")->count();
$bookedCount = $shift->assignments()->whereIn('status', ["Booked"])->count();
}
}
I am certain that there must be a better, faster way. Some of these queries are taking upwards of 30+ seconds. There are hundreds of thousands of Assignments, which I know is affecting performance. How can I speed up these queries?
You're running into the N+1 issue here a few times. You want to lazy load the nested assignment through jobs then you can access the relation and your where() and whereIn() calls are executed on the returned collection instead of on the query builder which is why you have to use where('status', "Pending") instead of whereStatus("Pending") in my example because the collection won't automatically resolve this constraint:
$job = Job::with('roles.assignments')->find($jobId);
foreach ($job->roles as $role) {
$pendingCount = $role->assignments->where('status', "Pending")->count();
$bookedCount = $role->assignments->whereIn('status', ["Booked"])->count();
}
This should be a lot quicker for you.
UPDATE
You could even take that one step further and map the result and store the results in a property on the role:
$job->roles->map(function($role) {
$role->pending_count = $role->assignemnts->where('status', "Pending")->count();
$role->booked_count = $role->assignments->whereIn('status', ["Booked"])->count();
return $role;
});

Magento - Get Country Id from Name - is there a easier/quicker way?

I need to load the 2 letter ISO2 country ID for a given country name.
Right now, I use the following method to do this:
// Method to Get countryId from CountryName
function getCountryId($countryName) {
$countryId = '';
$countryCollection = Mage::getModel('directory/country')->getCollection();
foreach ($countryCollection as $country) {
if ($countryName == $country->getName()) {
$countryId = $country->getCountryId();
break;
}
}
$countryCollection = null;
return $countryId;
}
Usage:
var_dump(getCountryId('Germany'));
Outputs:
string(2) "DE"
Is there a easier/quicker way to do this instead of loading the country collection and iterating through it every time?
Surprisingly, looping is the only way you'll achieve this.
The country names are stored in XML files in lib/Zend/Locale/Data/ and they're organized by locale (en, es, fr) and then country code, not country name.
Since it's not a SQL table you can't add a WHERE clause (using Magento's addFieldToFilter()), so you'll be looping through the XML nodes anyway.
There is no other way, because of translations. This is function for getting country name, you can't reverse it
public function getName()
{
if(!$this->getData('name')) {
$this->setData(
'name',
Mage::app()->getLocale()->getCountryTranslation($this->getId())
);
}
return $this->getData('name');
}

How can I create an Expression within another Expression?

Forgive me if this has been asked already. I've only just started using LINQ. I have the following Expression:
public static Expression<Func<TblCustomer, CustomerSummary>> SelectToSummary()
{
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = // This is a Person entity, no idea how to create it
});
}
I want to be able to populate LastSalesContact, which is a Person entity.
The details that I wish to populate come from m.LatestPerson, so how can I map over the fields from m.LatestPerson to LastSalesContact. I want the mapping to be re-useable, i.e. I do not want to do this:
LastSalesContact = new Person()
{
// Etc
}
Can I use a static Expression, such as this:
public static Expression<Func<TblUser, User>> SelectToUser()
{
return x => (new User()
{
// Populate
});
}
UPDATE:
This is what I need to do:
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = new Person()
{
PersonId = m.LatestPerson.PersonId,
PersonName = m.LatestPerson.PersonName,
Company = new Company()
{
CompanyId = m.LatestPerson.Company.CompanyId,
etc
}
}
});
But I will be re-using the Person() creation in about 10-15 different classes, so I don't want exactly the same code duplicated X amount of times. I'd probably also want to do the same for Company.
Can't you just use automapper for that?
public static Expression<Func<TblCustomer, CustomerSummary>> SelectToSummary()
{
return m => Mapper.Map<TblCustomer, CustommerSummary>(m);
}
You'd have to do some bootstrapping, but then it's very reusable.
UPDATE:
I may not be getting something, but what it the purpose of this function? If you just want to map one or collection of Tbl object to other objects, why have the expression?
You could just have something like this:
var customers = _customerRepository.GetAll(); // returns IEnumerable<TblCustomer>
var summaries = Mapper.Map<IEnumerable<TblCustomer>, IEnumerable<CustomerSummary>>(customers);
Or is there something I missed?
I don't think you'll be able to use a lambda expression to do this... you'll need to build up the expression tree by hand using the factory methods in Expression. It's unlikely to be pleasant, to be honest.
My generally preferred way of working out how to build up expression trees is to start with a simple example of what you want to do written as a lambda expression, and then decompile it. That should show you how the expression tree is built - although the C# compiler gets to use the metadata associated with properties more easily than we can (we have to use Type.GetProperty).
This is always assuming I've understood you correctly... it's quite possible that I haven't.
How about this:
public static Person CreatePerson(TblPerson data)
{
// ...
}
public static Expression<Func<TblPerson, Person>> CreatePersonExpression()
{
return d => CreatePerson(d);
}
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = CreatePerson(m.LatestPerson)
});

Resources