I want to retrieve all the tasks details (task_title etc) belonging to one author_id (author2 in this case).
Tests table
author_id task_id
author2 task_1
author2 task_2
Tasks table
task_id task_title
task_1 task_title_1
task_2 task_title_2
Author table
author_id author_name
author_2 authorTwo
Model test.php
public function tasks()
{
return $this->belongsTo('Task','task_id');
}
TestsController.php
public function index()
{
$test=Test::find('author2')->tasks()->get();
return View::make('tests.index', compact('tests'));
}
and the query SQL:
select * from `tests` where `author_id` = 'author2' limit 1
select * from `tasks` where `tasks`.`task_id` = 'task1'
But actually in tasks table, there are more than one value related to author 2 (in this case task 1 and task2 ) but as the sql only illustrates task1.
How can I remove the limit 1 restriction to retrieve all the tasks belonging to author 2?
There are multiple issues with that. The main ones being:
You're using the wrong relationship for a many-many, look into belongsToMany()
It looks like you're trying to look up on a composite key which is not supported
Find is intended to fetch only one record, and does not limit the result set of a relationship. As shown by your SQL, you're not limiting your tasks by one, you're limiting tests. Use get() in conjunction with where() when you need to fetch multiple records.
Here is a link to the documentation to get you started - http://four.laravel.com/docs/eloquent
Test::where("author_id","=","author2")->get()
You should not be using find for anything that is not a primary key, by the way. This is the aim of find: it fetches one item as it assumes that the key it searches for is primary auto-increment (i.e. unique)
Related
Suppose I have a Laravel 5.5 app with four models:
Class, Student, Assignment and AssignmentSubmission
A Class belongs to many Students, a Student belongs to many Classes and an AssignmentSubmission belongs to an Assignment, a User, and a Class.
So the tables look like this:
classes:
id
name
students:
id
name
class_student:
class_id
student_id
assignments:
id
name
assignment_submissions:
id
class_id
student_id
assignment_id
Now, in a query builder class I need to construct a query that returns the Students that belong to a Class with an extra column called total_assignment_submissions with a tally of how many assignments that student has submitted for that class only.
My first port of call was the following:
$class->students()->withCount('assignmentSubmissions')->get();
But that returns the total assignment submissions for that student for all classes.
The following mySql query returns the right data:
SELECT *,
(SELECT Count(*)
FROM `assignment_submission`
WHERE `student_id` = `students`.`id`
AND `class_id` = ?) AS total_assignment_submissions
FROM `students`
INNER JOIN `class_student`
ON `students`.`id` = `class_student`.`student_id`
WHERE `class_student`.`class_id` = ?
But I can't seem to get the selectSub() addSelect() and raw() calls in the right order to get what I want. It seems that I only do it with raw queries but the event id will be unsanitized or otherwise I can do it but it only returns the assignment_count field and not the rest of the fields for the student.
$class->students()
->selectSub(
AssignmentSubmission::selectRaw('count(*)')
->whereClassId($class->id)->getQuery(),
'total_assignment_submissions'
)->toSql();
There must be a way to do this. What am I missing here?
withCount() can be constrained.
$class->students()->withCount(['assignmentSubmissions' => function($q) use($course_id){
$q->where('course_id', $course_id);
}])->get();
This will limit the count to that particular course.
I am making an online directory, this directory contains businesses, this is how the current table structure is set out:
1) "Business"
ID (PK)
Name
Phone_Number
Email
2) Tags
id (PK)
tag
3) Business_tags
id (PK)
business_id (FK)
tag_id (FK)
There are over 9k rows inside of the business table, and over 84,269 rows and there are over 29k rows inside the ("Business_tags") table (As a business can have multiple tags).
Inside the business model, is the following:
public function tags()
{
return $this->belongsToMany('App\Tags');
}
The issue is when I am trying to do a search, so for example, let's say that someone wants to search for a "Chinese" then it's takes more time than it probably should to return a value. For example, I am using:
$business = Business::where(function ($business) use ($request) {
$business->whereHas('tags', function ($tag) use ($request) {
});
})->paginate(20);
Searching takes on average: 35 seconds to display the results.
Here is the raw sql:
select * from `businesses` where (exists (select * from `tags` inner join `business_tags` on `tags`.`id` = `business_tags`.`tags_id` where `business_tags`.`business_id` = `businesses`.`id` and `name` in ('chinese')))
This takes on average: 52.4s to run inside Sequel pro (Using the raw SQL statement)
Any ideas how I can improve the performance of this query so that it's a lot faster? I want to have this functionality, but the user is not going to wait this long for a response!
EDIT:
1 PRIMARY businesses NULL ALL NULL NULL NULL NULL 8373 100.00 Using where
2 DEPENDENT SUBQUERY business_tags NULL ALL NULL NULL NULL NULL 30312 10.00 Using where
2 DEPENDENT SUBQUERY tags NULL eq_ref PRIMARY PRIMARY 4 halalhands.business_tags.tags_id 1 10.00 Using where
You're over-complicating this, and not using eloquent relationships correctly. You should be using JOINs instead:
$businesses = Business::join('business_tags', 'business_tags.business_id', '=', 'business.id')
->join('tags', function($join) {
$join->on('business_tags.tag_id', '=', 'tags.id')
->where('tags.name', '=', 'chinese');
})->get();
Or in raw SQL:
SELECT *
FROM `business`
INNER JOIN `business_tags` ON `business_tags`.`business_id` = `business`.`id`
INNER JOIN `tags` ON `business_tags`.`tag_id` = `tags`.`id` AND `tags`.`name` = 'chinese'
(Note that you could put that tags.name = 'chinese' part in the WHERE clause and yield the same effect)
Your current query does an exists subquery to get all the records from the pivot table that match the criteria, then passing that back to the main query. It's an extra step, and it's unnatural.
Eloquent relationships are NOT for complex queries like this, but are rather there to provide additional, related information about a record without having to write another query manually.
For instance, if you want to view a business, you might query with() phone numbers and addresses from other tables. You might want to list out their tags, or sync() them. But eloquent does not build and filter queries, that's what query builder is for.
Let me know if you need more explanation.
As a lot of others are also going to tell you.
Have you run EXPLAIN on your query?
Have you added indexes to your tables?
Because even with the amount of data you have mentioned the query should have been faster than what you have reported.
Also see if a JOIN can work here and if faster?(just a thought)
I have subject table that contains
id name
and languages table that contains
id subject_id
and division table
id name
finally subject-division table (pivot table)
id subject_id division_id
now exist one-To-one relationship between subject table and languages table and many-To-many relationship between subject table and division table, i need to pluck only the subjects of subject table without the languages by using the relationship function
now i can get the languages only of the subject table of the relationship function In Division Model as the following
public function langSubject ()
{
return $this->belongsToMany('Subject' , 'subject_division','division_id','subject_id')
->join('lang_subject', 'lang_subject.subject_id' ,'=', 'subject.id')->get();
}
But till now I can't get the subjects only without the languages
Any Suggestions ?
You need to add the clause ->select('tableName1.fieldName1','tableName2.fieldName2','tableName3.fieldName3') to your statement after the join statement to get the tableName.fieldName and you may need to use leftJoin instead of join to get the results whether if there is a match or not.
Check out Eloquent relationship documentation -> sub headline "Querying Relationship Existence". There it mentions ::has method, which you could use in fashion
Division::has('subject.lang', '<', 1)->get()
Or at least that's the theory. Haven't had the need for it yet ;-)
It should be supported by Laravel 4 as well.
table
ID|owner_id|work_id|lorem|etc|
1 |00123 | 00213 |XXXXX|XXX|
2 |00124 | 00213 |XXXXX|XXX|
owner_id (fk) owners.id (owners[id,name,etc])
work_id (fk) work.id (work[id,name,etc])
question is can I set codeigniter that when I
select(table.*,work.name as work,owners.name as owner) from table
it automatically handle joins since that table already contain the fk-ref ? or I must include join('owner','owner.id=table.owner_id) ?
actually all what I want is that when I select a table that contains a fk this fk column is replaced with one column from ref row by just passing the column name on ref table without having to worry about creating a specific function in my module for that each query.
My current solution:
was to create a view for each table that contain a relation and replace all fk columns with desired ref value, but since i have 6 tables 5 of them with fk,i now have 6 tables and 5 view (11 tables)in db which is really kind confusing for me, so any smarter way to do this ?
I think you are making some confusion on what FK is and what it does within a table.
FK constraint grants data integrity when it's present and relates data within tables. It doesn't join anything.
If you want to select records across related tables, you either use a
SELECT * FROM table1,table2 WHERE table1.K1 = table2.FK1
or
SELECT * FROM table1 JOIN table2 ON table1.K1 = table2.FK1
AND YES, you need to tell CodeIgniter to do those queries
I have following tables
articles_category
id title sef_title
articles_data
id cat_id title sef_title details
On each table "id" is the primary key and articles_data.cat_id is foreign key of articles_category
I need to fetch one latest article data for each articles category with following data.
articles_category.id
articles_category.title
articles_category.sef_title
articles_data.id
articles_data.cat_id
articles_data.title
articles_data.sef_title
articles_data.details
I tried with following query but it displays first article (oldest entry) rather than latest one.
SELECT
articles_category.id as article_cat_id, articles_category.sef_title as cat_sef_title, articles_category.title as cat_title,
articles_data.id, articles_data.cat_id as listing_cat_id, articles_data.title, articles_data.sef_title, articles_data.details
FROM articles_category, articles_data
WHERE articles_category.id = articles_data.cat_id
GROUP BY articles_data.cat_id
ORDER BY articles_data.id DESC
if it's a one to many relation, try (untested):
SELECT *
FROM articles_category, articles_data
WHERE articles_category.id = articles_data.cat_id
AND articles_data.id in (
SELECT max(articles_data.id)
FROM articles_data GROUP BY cat_id
)
you are not guaranteed a particular row on a GROUP BY
and you should really use a date on your article as the max id is never guaranteed to be the latest article even if you are using autoincrement
ORDER affects the display of records after the GROUP function has been performed. The GROUP function recognizes the first record it sees in its search.
What you need to do is perform 2 queries. The first one should be
SELECT max(articles_data.id), articles_data.cat_id ...
GROUP BY articles_data.cat_id
The second query should fetch the associated records using those resultant primary keys.
A possibility is to create another value such as date_created and place a time stamp there when creating an entry.
Then you would just have to ORDER BY date.