Laravel eloquent selecting most recent row from joined table - join

I have two tables, Project and Projectnote
There is a one to many relationship between project and projectnote.
I want to be able to list my projects and select the most recent projectnotes based on the created_at date.
Is this possible to do in Eloquent, I can't figure it out.
Thanks for any help!
Edit: so far I have a basic query (below) that joins the two tables. However, this only selects projects where a note exists and I get multiple rows where there are several notes per project.
$projects = Project::join('projectnotes', 'projectnotes.project_id', '=', 'projects.id')
->select(array('projects.*', 'projectnotes.note as note'))
->get();

You can join the table on the latest value:
->join('t1', function($join) {
$join->on('t1.t2_id', '=', 't2.id')
->on('t1.id', '=', DB::raw("(select max(id) from t1 WHERE t1.t2_id = t2.id)"));
If you also want to join where t1 does not have a value, use a leftJoin. In your case this would look like this:
$projects = Project::leftJoin('projectnotes', function($join) {
$join->on('projectnotes.project_id', '=', 'projects.id')
->on('projectnotes.id', '=', DB::raw("(SELECT max(id) from projectnotes WHERE projectnotes.project_id = projects.id)"));
})
->select(array('projects.*', 'projectnotes.note as note'))

I would suggest you try to order by id in that specific scenario; the below is my suggested solution for you based on your code:
$projects = Project::join('projectnotes', 'projectnotes.project_id', '=', 'projects.id')
->select(array('projects.*', 'projectnotes.note as note'))
->orderBy('projectnotes.id')
->latest('projectnotes.id')
->get()->unique();
The code above works on Laravel 5.6; your can try it out.

In your project model, make the notes relationship function look something like:
public function notes() {
return $this->hasMany('Note');
}
Then you can get the most recent Note of the Project with primary key 1 by chaining orderBy and first:
Project::find(1)->notes()->orderBy('created_at', 'desc')->first();
Also, is your note table called 'Note' or 'Notes'? (Keeping with Laravel style, it should be named singular as I have it in this code)

Related

Convert SQL to ActiveRecord

What is the most appropriate way to convert the following SQL to an ActiveRecord query?
select count(*) from products where id in (
select product_id from store_locations
where store_id in (
select id from stores where store_definition_id = 1
)
)
This uses joins instead of sub queries, but if you're associations are setup correctly should work. I may have messed up the syntax a little bit, but I think it's right.
Product.joins(store_locations: :stores).where(stores: {store_definition_id: 1}).count
EDIT
The above is going to return more rows than you want as it each store_locations row will be returned with the product. Not good. So perhaps:
Product.where(id: StoreLocation.joins(:store).where(store: {store_definition_id: 1}).pluck(:product_id)).count

Duplicate entity on join request with Query Builder

I'm using the Laravel's query builder with this syntax :
$article = DB::table('users')->join('articles', 'articles.id', '=', 'users.id')->where('article_id', $id)->first();
You can see that both articles and users table have an id column.
Then in my view I use the query result, and I only get one id whereas I need both.
Is there any way to get them ?
I know I could use :
->select('articles.id as myFirstId', 'users.id as mySecondId', 'users.name', ...)
But there are many columns in this request, and if I can avoid it I'd prefer.
Thanks
Raphaƫl N.
You can select all fields from the table with higher number of fields with * FROM table and then pick one by one from the other table, like this:
$article = DB::table('users')
->join('articles', 'articles.id', '=', 'users.id')
->where('article_id', $id)
->select('articles.*', 'users.id as userId', 'users.name') // Add rest of `users` fields
->first();

Difficulties with SQL query and COUNT

thanks in advnace for reading this. I'm not really good with SQL so please pardon any stupid mistake...
Here is the deal, I have four tables (i'm only going to give the basic fields, and dependencies between tables, for the sake of simplicity):
Company: companyId, companyName
User: userId, userName
Project: projectId, projectUserId, projectCompanyId, projectDate
Study: studyProjectId
The dependencies are like so:
A project is for a client (projectUserId) and carried out by a company (projectCompanyId)
There can be many studies for the same project, but each study is for one project (studyProjectId)
Here is the kind of request I'd like to write, but it doesn't work right now:
SELECT
project.projectId,
company.companyName,
user.userName,
COUNT( study.studyId ) AS numberStudies
FROM project, company, user, study
WHERE company.companyId = project.projectCompanyId,
AND user.userId = project.projectUserId,
AND study.studyProjectId = project.projectId
ORDER BY company.companyId, user.userId, project.projectDate;
It returns one record for which numberStudies equals the total number of studies. If I remove the COUNT from the SELECT, then I get the type of result I want, but without the column numberStudies (of course). Hoping you understand what I'm trying to get, what am I doing wrong here?
Thanks again in advance :)
EDIT: If possible, I'd like the request to show records even when numberStudies is 0.
As in the comments, you need a GROUP BY clause when you want to have aggregate results (like it seems you want: "Number of Studies per project, company and user"). So, the first thing to do is add:
GROUP BY project.projectId, company.companyName, user.userName
Notice that the three columns are exactly the three that you have (unaggregated) in the SELECT list.
SELECT
project.projectId,
company.companyName,
user.userName,
COUNT(study.studyId) AS numberStudies
FROM project, company, user, study
WHERE company.companyId = project.projectCompanyId,
AND user.userId = project.projectUserId,
AND study.studyProjectId = project.projectId
GROUP BY project.projectId, company.companyName, user.userName
ORDER BY company.companyId, user.userId, project.projectDate ;
This will show what you want but there are still a few issues:
First, you are using the old (SQL-89) syntax of implicit joins with the conditions in the WHERE clause. This syntax is not deprecated but the new (well, 20 years old SQL-92) syntax with the JOIN keyword has several advantages.
We can add aliases for the tables for readability.
There may be two companies or users with same name so we should group by their IDs, not only their names.
One advantage of explicit JOIN syntax is that it's easy to have results when there are no rows to join (as you want to show when there is no studies for a project). Just LEFT JOIN the study table.
So, the query becomes:
SELECT
p.projectId,
c.companyName,
u.userName,
COUNT(s.studyId) AS numberStudies
FROM
project AS p
JOIN
company AS c ON c.companyId = p.projectCompanyId
JOIN
user AS u ON u.userId = p.projectUserId
LEFT JOIN
study AS s ON s.studyProjectId = p.projectId
GROUP BY
c.companyId,
u.userId,
p.projectId,
c.companyName, u.userName
ORDER BY
c.companyId,
u.userId,
p.projectDate ;
you probably need a LEFT JOIN between study and project

COUNT and GROUP BY using Zend Framework 2 and tableGateway

In Zend Framework 2, using tableGateway, I want to run the following SQL query:
SELECT categories.category_name, COUNT(forums.forum_id)
FROM categories LEFT JOIN forums
ON categories.category_id = forums.category_id
GROUP BY categories.category_name;
Problem is that I simply don't know how to do it. I know how to use $select->join() for example, but I can't figure out how to also do a COUNT and GROUP BY.
What I want with my SQL: I have 2 tables; categories and forums. I want to select all the categories from categories and for each category I want the amount of forums.
Someone on another forum gave me the correct answer, and this works for me. Thought I would share it in case anyone else is having a similar question. Here is how I have it now:
use Zend\Db\Sql\Expression;
$resultSet = $this->tableGateway->select(function (Select $select)
{
// Select columns and count the forums.
$select->columns(array(
'category_name',
'forumsCount' => new Expression('COUNT(forums.forum_id)')
));
// Left-join with the forums table.
$select->join('forums', 'categories.category_id = forums.category_id', array(), 'left');
// Group by the category name.
$select->group('categories.category_name');
});
return $resultSet;
Your query looks right. Does it work as expected when you run it directly on the database.
I think you might just need to execute the raw query using an adapter.
$sql = "SELECT categories.category_name, COUNT(forums.forum_id) FROM categories LEFT JOIN forums ON categories.category_id = forums.category_id GROUP BY categories.category_name";
$statement = $this->adapter->query($sql);
return $statement->execute();

Selecting multiple values different tables

I'm relatively new to MySql.
I have 2 tabled with the following structure
products
{
pid-autoincrement,
pname
p_desc
}
services
{
sid-autoincrement
s_name
s_desc
}
Im trying to select the products or services which have the name '%<somekeyword>%'
I'm using the query:
SELECT DISTINCT products.*,
services.*
FROM products, services
WHERE products.pname
LIKE '%mob%'
OR services.s_name
LIKE '%mob%'
But I'm getting a lot of repeated results.
Tried using joins but couldn't get a solution.
Can someone help me with this?
You want to use UNION, like this:
SELECT DISTINCT products.pid-autoincrement AS id,
products.pname AS name,
products.p_desc AS desc
FROM products
WHERE products.pname
LIKE '%mob%'
UNION
SELECT DISTINCT services.sid-autoincrement AS id,
services.s_name AS name,
services.s_desc AS desc
FROM services
WHERE services.s_name
LIKE '%mob%'
you have to use the UNION operator
therefore you can only select the same columns (as there is only one result)
or you select 2 times - 2 results
Because the tables aren't related you'll have to use the UNION operator (as karlis says), rather than JOIN. But as you apparently want to handle products and services together (at least some of the time) you're probably better off putting them into one table and adding a column to differentiate between them, like so:
productsandservice [need better name]
{
id,
name,
desc,
isaservice
}

Resources