Having XML with book nodes and each book can have multiple authors. How do I find books with at least one author starting with letter E?
for $book in /library/book[starts-with(author,'E')]
return $book
This doesnt seem to work with multiple authors...
Thanks in advance.
Predicates can be nested.
for $book in /library/book[author[starts-with(., 'E')]]
return $book
or, more defensively:
for $book in /library/book[author[starts-with(normalize-space(.), 'E')]]
return $book
Related
I have a project where I have the following Models :
Products
FeatureTypes
Features
Variants
And the tables and relationships are the following :
I have a request where I want to retrieve the Variants from a Product with some specific Features.
A Variant can have 1 or many Features but I want to have all Variants if I give one Feature, and have a more specific search on the Variants if I gave more Features, until I have only one Variant which can have only one set of specific Feature.
Here's the code I have for now :
$variants = $product
->variants()
->whereHas('features', function($query) {
$query->whereIn('id', json_decode(request('features')));
})
->with('features', 'features.featureType')
->get();
The request('features') contains a stringified array with ids of Features.
I used Eloquent's whereIn method thinking that it would give me only the variants that have exactly all the Features given in the request.
But while checking the documentation again, I see that it will return any Variants that has at least one Feature given in the request which is not what I need here.
How can I make that Eloquent's query to return me only the Variants that are associated with all the Features given by the request ?
Thanks in advance for you help ! ;)
whereHas() accepts a fourth argument with the number of rows:
$variants = $product
->variants()
->whereHas('features', function($query) use ($features) {
$query->whereIn('id', $features);
}, '=', count($features))
->with('features', 'features.featureType')
->get();
I'm sure this may be a simple solution, but I can't seem to work it out.
I am trying to use Laravel's where() clause to build an array of $courses that belong to each $student. I cycle through each $student and filter the $courseRecords to find matching courses based on their StudentCode.
Here is my sample code snippet:
// Cycle through the students and add their relevant course details
foreach( $students as $student ) {
// Find matching courses to the student
$courses = $courseRecords->where( 'StudentId', $student->StudentCode );
// Add the course array to the student record
$student->Courses = $courses;
}
However, the result I get, gives me each student's course, but with a leading index number (as shown below in random results):
I can't seem to work out why this is happening. The first entry (Id 0) is the result I am expecting, but for some reason, every other result seems to give me the matching index number of $courseRecord.
I have tried using $courses->all(); and $courses->toArray(); but this doesn't make any difference. From the Laravel documentation (that I have read), it doesn't mention this behaviour which makes me think I have something incorrect.
$students and $courseRecords are both a collection.
Use values():
$courses = $courseRecords->where('StudentId', $student->StudentCode)->values();
If an author has many books ("one to many" relationship) and I want to create a child by $author_id I should do this:
$author = Author::find($author_id);
$author->books()->create([...]);
But this code produces two SQL-requests as well as this:
Author::find($author_id)->books()->create([...]);
To reduce the number of SQL-requests I should add author_id field into the $fillable array in my Book model and do this:
Book::create([
'author_id' => $author_id,
...
]);
Which approach is better? As for me, the first one looks more correct, more Eloquent way, but 2 SQL-requests are too much for such simple case. Are there any other ways to make only one SQL-request without touching the $fillable array?
The old school:
$book = new Book;
$book->author_id = $author_id;
//...
$book->save();
Or you could forceCreate, which bypasses fillable:
Book::forceCreate(['author_id' => $author_id, ...]);
As for which approach is better: if you know the author_id, then 2nd (without using the relationship). But from my experience, that's rarely the case, since you usually want to check whether the related model actually exists. But if you're confident in the correctness of your input, no need for 2 queries.
I have a project with the classes Semester, Course, and Assignment.
I wanted to get only Semesters that had at least one Course associated with it, so I did this:
Semester::has('courses', '>', '0')->get();
Now, I want get Semesters that have at least one Course that has at least one Assignment associated with it.
In the Semester class, I have a hasMany relationship with Course, and in Course, I have a hasMany relationship with Assignments. I also have a belongsTo for Assignment to Course and Course to Semester.
I have tried looking up closures, but none of the tutorials seem to relate to checking a collection of a collection. How would I run this code?
EDIT
After reading and testing more, I found that this code works:
Semester::whereHas('courses', function ($query) {
$query->has('assignments', '>', '0')
})->get();
huuuk's answer is a different approach but equally valid and helpful.
First of all define hasManyThrough relation in Semetster class
public function assignments()
{
return $this->hasManyThrough('App\Course', 'App\Assignment');
}
then you can reach your goal like so
Semester::has('assignments', '>=', '1')->get();
In Laravel 4.2 I have a model called Product with many-to-many relationshis to other models like Country or Category. I want to filter out products that are "incomplete", which means they have no connected countries or no connected categories. I can use whereDoesntHave() method to filter out one relation. When I use it two times in one query it creates AND condition, but I need OR. I can't find orWhereDoesntHave() method in API documentation. I can't pass multiple relations as arguments because it expects first argument to be a string.
I need something like this:
$products = Product::whereDoesntHave('categories')->orWhereDoesntHave('countries')->get();
Is there any way to achive whereDoesntHave() with multiple OR conditions?
You can use doesntHave and specify the boolean operator:
$products = Product::doesntHave('categories')->doesntHave('countries', 'or')->get();
Actually you only need whereDoesntHave if you want to pass in a closure to filter the related models before checking if any of them exist. In case you want to do that you can pass the closure as third argument:
$products = Product::doesntHave('categories', 'or', function($q){
$q->where('active', false);
})->doesntHave('countries', 'or')->get();
Since Laravel 5.5 there is an orWhereDoesntHave function.
You may use it like this
Product::whereDoesntHave('categories', function($q){ //... })
->orWhereDoesntHave('countries', function($q){//...})
->get();
From you example it seems that you are not using a where clause, so you may just use
Product::doesntHave('categories')
->orDoesntHave('countries')
->get();
Use
Product::whereDoesntHave('categories')->doesntHave('countries', 'or')->get();
Laravel Source Code:
whereDoesntHave https://github.com/illuminate/database/blob/master/Eloquent/Builder.php#L654
calls
https://github.com/illuminate/database/blob/master/Eloquent/Builder.php#L628
internally.
Let’s say we have Authors and Books, with 1-n relationship – one Author can have one or many Books. Here’s how it looks in app\Author.php:
public function books()
{
return $this->hasMany(\App\Book::class, 'author_id');
}
Now, what if we want to show only those Authors that have at least one book? Simple, there’s method has():
$authors = Author::has('books')->get();
Similarly, there’s an opposite method – what if we want to query only the authors without any books? Use doesnthave():
$authors = Author::doesnthave('books')->get();
It’s not only convenient, but also super-easy to read and understand, even if you’re not a Laravel developer, right?