Adding a custom query to the model - php

For simplicity's sake, I have two tables: projects and tasks. A project can have many tasks, and a task belongs to a project.
I've set up the database, created the associations and used cake bake to generate the models, controllers and views and all is perfect.
When I look at a the index view for projects, I see a table listing all the projects as expected. What I want to do is really simple: I want a column in that table that shows a count of all the tasks assigned to that project.
The SQL query for this is trivial (returning a list of project names and a count of the tasks):
SELECT projects.name,
(SELECT COUNT(*) FROM tasks WHERE project_id = projects.id) as taskCount
FROM projects
So how do I achieve this in CakePHP?
The index method in the projects controller looks like this at the moment:
function index() {
$this->Project->recursive = 0;
$this->set('projects', $this->paginate());
}

You can use the virtualFields or counterCache, but it is another way.

Related

belongsToMany with multiple tables (design suggestion)

I have 5 tables in my laravel/vue project:
Movies
Series
Anime
Actors
Actors relationship
Actors' table belongs to Movies, Series, Anime and this many-to-many relationship is registered in the actors_relationship.
I'm able to create a many-to-many relationship in the Actor.php model using the following code:
public function movies() {
return $this->belongsToMany(MovieModel::class, "actors_relationship", 'actor_id', 'media_id')->wherePivot("media_type", AdminHelper::TYPE_MOVIES['value'])->withTimestamps();
}
Using this code I'm getting the records with the included actor id and has a specific int as a media_type.
Until this point, I have no problem. My problem is getting the count of all of these relationships. e.g. getting the count of movies, series, anime and sorting it desc/asc
Solutions:
Retrieving the relationship actors' count from movies, series, anime and register it in a custom attribute. The sorting will be on the client-end.
Doing a hard code check on the server-side for the sorting, then checking if the sort is by total_count, if yes, then get the collection first and after the ->get() command, try sorting by the custom attribute.
I would like to follow the best practice regarding this issue. Is there a way to get the count of all relationships and sort by it?
A client-side solution would cause more work because this project will be for Android and Web and therefore will require various modifications in both fields.
Furthermore, I would normally do ->with('movies') and then sort the automatically created field movies_count. I would like to have a similar approach.
Tables:
actors:
id actor_title
movies:
id movie_title
series:
id series_title
anime:
id anime_title
actors_relationship:
id actor_id media_id media_type
EDIT:
I'm looking for the best advice to implement a total count of movies, series and anime. This means that I would like the total count of these 3 relations in one field. I could use a custom attribute, but then I will need to sort that using PHP/client-side instead of doing it in the SQL query in Eloquent.
This means that I'm looking for the best way that follows the best practices.
The total_count will sum up the total of those aforementioned relations. I would like to sort by this new total_count.
This seems like a good case for a MorphToMany https://laravel.com/docs/5.8/eloquent-relationships#many-to-many-polymorphic-relations
class Actor
public function movies()
{
return $this->morphedByMany(MovieModel::class,'media',"actors_relationship");
}
/// Add a the count to the query
public function scopeWithRelationsCount($q)
{
/// this is needed to so we keep the select of all the table columns too
if(!$q->getQuery()->columns){
$q->select($this->getTable().'.*');
}
return $q->selectSub(
ActorRelationship::selectRaw('count(*)')->whereColumn('actor_id', 'actors.id'),
'relations_count' /// the alias we are using could be anything
)
}
Then you if you want the total number of series, movies and and anime for each actor ordered by most you can use.
Actor::withRelationsCount()->orderBy('relations')->get()
There are some macros that make this look a little nicer. Take a look at https://github.com/reinink/advanced-eloquent.

Laravel select from DB where in Collection - Laravel Nova

I have a DB, "views," with many, many entries. I also have a "Courses" table, which these views are one-many related to. In Laravel Nova, I can get a metric of all views over time for a course with some code like this:
public function calculate(Request $request)
{
return $this->countByDays($request, view::where('viewable_id', $request->resourceId));
}
In this case, viewable_id is the id of the course, and $request->resourceId gives the ID of the course to sort by. Pretty simple.
However, now things get a little difficult. I have another model called Teachers. Each Teacher can have many courses, also in a one-many relationship. How do I get a metric of views over time for all the courses that teacher teaches?
I assumed the simplest way to do this would be to create a Laravel Collection with all courses the Teacher teaches (not exactly efficient), and then select all views in the database where viewable_id matches one of the courses in that list. Of course, by posting this, I couldn't figure out how to do that.
Of course, once this is figured out, I'd love to do the same thing for Categories (though that should function in a very identical manner to Teachers, so I don't need to ask that question).
How do I get a metric of views over time for all the courses that teacher teaches?
This should be the "countByDays" of views where the viewable_id is in the list of course ids that the teacher teaches.
An SQL query statement to achieve that is given below:
select * from "views"
where "viewable_id" in (select "id" from "courses" where "teacher_id" = ?)
The Eloquent query should be similar to:
$this->countByDays($request,
view::whereIn(
'viewable_id',
Course::with('teacher')
->select('id')
->where('teacher_id', $request->resourceId)
)
);

get *related* linked models in laravel eloquent instead of raw SQL

I'm trying to get 'related' linked models by querying a link table, named company_projects which holds (as you expect) the id's of companies and projects (projects are kind of product-categories).
In this case, the used flow to determine a related project is:
Get companies who are in the same project ('product category') as you
Find the other project id's which are linked to those companies
Get the info of the linked projects fetched by last step
What i'm trying to do is already functional in the following raw query:
SELECT
*
FROM
projects
WHERE
projects.id IN
(
SELECT cp1.project_id
FROM company_projects cp1
WHERE cp1.company_id IN
(
SELECT cp1.company_id
FROM projects p
LEFT JOIN company_projects cp2 ON cp2.project_id = p.id
WHERE p.id = X AND cp2.company_id != Y
)
)
AND projects.id != X
X = ID of current project ('product category')
Y = ID of current 'user' (company)
But my real question is, how to do this elegantly in Laravel Eloquent (currently v4.2). I tried it, but I have no luck so far...
Update:
I should note that I do have experience using Eloquent and Models through multiple projects, but for some reason I just fail with this specific query. So was hoping to see an explained solution. It is a possibility that I'm thinking in the wrong way and that the answer is relatively easy.
You will need to utilize Eloquent relationships in order to achieve this. (Note that I am linking to the 4.2 docs as that is what you are using, but I would highly suggest upgrading Laravel to 5.1)
I am assuming you have a 'Company' and 'Project' model already. Inside each of those models, you need to a define a method that references its relationship to the other model. Based on your description, it sounds like the two have a Many to Many relationship, meaning that a company can have many projects and a project can also belong to many companies. You already have a database table linking the two. In the Eloquent ORM this linking table is called a pivot table. When you define your relationships in the model, you will need to pass the name of that pivot table as your second argument. Here's how it could look for you.
Company model:
class Company extends Model
{
/**
* Get the projects attached to a Comapny. Many To Many relationship.
*/
public function projects()
{
return $this->belongsToMany('Project','company_projects');
}
}
Project model:
class Project extends Model
{
/**
* Get the companies this project belongs to. Many To Many relationship.
*/
public function companies()
{
return $this->belongsToMany('Company','company_projects');
}
}
If your models have these relationships defined, then you can easily reference them in your views and elsewhere. For example, if you wanted to find all of the projects that belong to a company with an ID of 1234, you could do this:
$company = Company::find(1234);
$projects = $company->projects;
Even better, you can utilize something called eager loading, which will reduce your data lookup to a single line (this is mainly useful when passing data to views and you will be looping over related models in the view). So those statements above could be rewritten as:
$company = Company::with('projects')->find(123);
This will return your Company model with all its related products as a property. Note that eager loading can even be nested with a dot notation. This means that you can find all the models that link to your main model, and then all the models for those models, and so on and so forth.
With all of this in mind, let's look at what you specifically want to accomplish.
Let us assume that this method occurs in a Controller that is being passed a project id from the route.
public function showCompaniesAndProjects($id)
{
//Get all companies in the same project as you
//Use eager loading to grab the projects of all THOSE companies
//Result will be a Project Object with all Companies
//(and those projects) as Eloquent Collection
$companies = Project::with('companies.projects')->find($id);
//do something with that data, maybe pass it to a view
return view('companiesView')->with('companies',$companies);
}
After defining your relations in your models, you can accomplish that whole query in a single line.

In Laravel Eloquent, select "whereIn" from parent table

In my Laravel project (with MySQL database), I have a few models: Time Entries, Tasks, and Projects.
Time Entries belong to Tasks
Tasks belong to Projects
so each table contains a column for the corresponding ID of its parent.
I have an array of Project IDs, and I am trying to select the time entries which, through their tasks, belong to those projects.
In other words, I'd like to be able to do something like this:
$timeEntries = TimeEntry::whereIn('project_id',$projectIds)->get();
But obviously, I get a column not found error, because all I've got in the time entries table is task_id rather than project_id.
Is there a way to select the desired time entries (based on the project IDs I have) in a single Eloquent query? Help much appreciated.
Add the following method in your Project model
public function timeEntries()
{
return $this->hasManyThrough('App\TimeEntry' , 'App\Task');
}
now you can get all time entries of a project like below
$project = Project::find(id);
$project->timeEntries()->get();
So the type of relation you're explaining is a through relation (http://laravel.com/docs/5.1/eloquent-relationships#has-many-through).
Instead of trying to head up the tree head down the tree from projects->tasks->time_entries.
Projects::whereIn($projectIds)->with('time_entries')->get();
The resulting collection will be projects with (should be at least) a field called time_entries in each project that have all the relevant times.

Laravel join tables

I have 2 tables and I need to join those tables.
I need to select id and name from galleries, where share gal.id = galleries.id and user_id = auth::id().
Tables:
Galleries: id, name
Share: gal_id, user_id
Please show me example in laravel. And I need to display it.
To achieve this, you have Relationship in Eloquent ORM. The official website states :
Of course, your database tables are probably related to one another. For example, a blog post may have many comments, or an order could be related to the user who placed it. Eloquent makes managing and working with these relationships easy. Laravel supports many types of relationships:
You can read all about eloquent relationship over here
If you do not want to use relationship, following is an example on how to join two tables :
DB::table('users')
->select('users.id','users.name','profiles.photo')
->join('profiles','profiles.id','=','users.id')
->where(['something' => 'something', 'otherThing' => 'otherThing'])
->get();
The above query will join two tables namely users and profiles on the basis of user.id = profile.id with two different conditions, you may or may not use where clause, that totally depends on what you are trying to achieve.

Categories