Laravel Eloquents: Check if child relationship has no data, Dont return any data - laravel

I have three entities Inbox , Document , Category
inbox has relation with document and document has many categories,
I have an Eloquent query that check relation between inbox and document and return inbox with documents data,
The Problem is:
I want to list all inbox with documents data only if document has no category (if it has one or more categories i dont want list this document with it's inboxes).
So i have this query now, that is not works correctly:
public static function getUserAndJobPositionsInbox($id, $type)
{
return self::with(['documents' => function($q){
$q->has('categories', '==', 0);
}])->where('inboxable_id', $id)
->where('inboxable_type', $type)->get();
}
This query will return inbox data with no documents if the documents has no categories
BUT i don't want inbox data if documents has no categories. just return empty
The result is :
{
"data": {
"user_inbox_documents": null,
"job_positions_documents": [
[
{
"id": 2,
"document_id": 3,
"document_reference": "RPL2p",
"inboxable_id": 3,
"inboxable_type": "App\\Models\\JobPosition",
"path_id": "2",
"created_at": "2018-09-12 13:21:23",
"updated_at": "2018-09-12 13:21:23",
"documents": []
}
],
[
{
"id": 1,
"document_id": 3,
"document_reference": "RPL2p",
"inboxable_id": 2,
"inboxable_type": "App\\Models\\JobPosition",
"path_id": "1",
"created_at": "2018-09-12 13:21:23",
"updated_at": "2018-09-12 13:21:23",
"documents": []
}
]
]
}
}
Thanks in advance.

You can check the existence of nested relationships using dot (.) for example following code (Laravel documentation) gets all the posts that have at least one comment and that comment should have atleast one vote.
// Retrieve all posts that have at least one comment with votes...
$posts = App\Post::has('comments.votes')->get();
So from above example, you can modify you code as follows:
public static function getUserAndJobPositionsInbox($id, $type)
{
return self::has('documents.categories')
->where('inboxable_id', $id)
->where('inboxable_type', $type)->get();
}

Related

Array with single attribute of many-to-many Eloquent relationship

I created a model NearMiss that has a many-to-many relationship with a Type model. When there is a GET request for a specific NearMiss, I want an array to be attached to the JSON output that has the name attribute of all Type instances that belong to the NearMiss instance.
I can attach an array of Types to the output, but I want to get rid of all the additional information (such as pivot information).
The GET request /nearmisses/{nearmiss} executes the following method:
public function show($id)
{
try {
$nearmiss = NearMiss::findOrFail($id)->first();
$nearmiss->types->makeHidden(['id', 'created_at', 'updated_at']);
return response()->json($nearmiss);
} catch(ModelNotFoundException $e) {
abort(400, 'Model not found');
}
}
The NearMiss model has the following types relation:
public function types()
{
return $this->belongsToMany('App\Type', 'near_miss_type', 'near_miss_id', 'type_id');
}
Current output:
{
"id": 1,
"location_long": "0.0000000",
"location_lat": "0.0000000",
"employee_id": 1,
"created_at": "2019-02-11 16:38:24",
"updated_at": "2019-02-11 16:38:24",
"types": [
{
"id": 1,
"name": "Brandgevaar"
"created_at": "2019-02-11 16:33:25",
"updated_at": "2019-02-11 16:33:25",
},
{
"id": 2,
"name": "Slipgevaar",
"created_at": "2019-02-11 16:34:12",
"updated_at": "2019-02-11 16:34:12",
}
]
}
I know that I can remove additional attributes (such as id, created_at, updated_at), but that still leaves me with an array of Type instances (with just a single name attribute). When I try flatten() I receive an error that flatten() can't be used on BelongsToMany relationships.
Desired output:
{
"id": 1,
"location_long": "0.0000000",
"location_lat": "0.0000000",
"employee_id": 1,
"created_at": "2019-02-11 16:38:24",
"updated_at": "2019-02-11 16:38:24",
"types": [
"Brandgevaar", "Slipgevaar"
]
}
Can someone help me to get the desired output please.
You could load the types manually and use the pluck() function to only get the name of each type.
$nearmiss->types = $nearmiss->types()->pluck('name');
However I'm not sure if this will interfere with the magic properties from Eloquent.

Send multiple variable to laravel Api resources

I want to different type of collection..One containing users acros the city and another one containing near by users. I want these from single api hit. is it possible ? If yes then please suggest how to do that.
Waht I did
return ServiceProviderCollection::collection($near_by);
Output:
"data": [
{
"username": "??",
"email": "??",
"rating": 0,
"role_id": 2,
"wallet": "0"
}
],
I want
return ServiceProviderCollection::collection($near_by,$across_city);
expected output:
{
"across_city": {
"data": [
{
"username": "??",
"email": "??",
}
],
},
"near_by": {
"data": [
{
"username": "??",
"email": "??",
}
],
}
}
No, you can't pass 2 objects in Resource. You can do it like this
return [
'across_city' => ServiceProviderCollection::collection($across_city),
'near_by' => ServiceProviderCollection::collection($near_by)
];
Edit: After comment
If you want to show pagination information then you have to create separate controller action and then return ServiceProviderCollection::collection then you will get result with pagination meta information.
create these action in your controller ex. (UserController)
public function acrossCity(){
$acrossCity = User::where('city','test')->paginate(10); //example
return ServiceProviderCollection::collection($acrossCity);
}
public function nearBy(){
$nearBy = User::where('near','1')->paginate(10); //example
return ServiceProviderCollection::collection($nearBy);
}
create routes for these
Route::get('user/acrossCity','UserController#acrossCity');
Route::get('user/nearBy','UserController#nearBy');
Check document https://laravel.com/docs/5.6/eloquent-resources#pagination
Note: when using resource class then name it without Collection. For your case you should name resource as ServiceProviderResource and then when you call its collection then ServiceProviderResource::collection($object) but when returning single object then new ServiceProviderResource($object).
I am currently using Laravel 7 and in my controller I am passing an array of collection objects to resource class
$data = ['quotation' => Quotation::first()];
return new QuotationResource($data);
and in my resource class I can access the data using
public function toArray($request)
{
return [
'quotation' => $this->resource['quotation']
];
}

Return a Boolean instead of the whole collection

The Stack model can have "user progress", users can "clear" stacks. And that progress is stored in the Progress model.
Progress model:
public function stack(){
return $this->belongsTo(Stack::class);
}
It would be neat if it was possible to fetch all stacks, and if the user has "cleared" it or not, with a boolean true or false. I don't won't to fetch all data corresponding to the progress. So I have tried setting up a "finished" relation on the Stack model
public function finished(){
return $this->hasOne(Progress::class) ? true : false;
}
But this setup gives me the following error
Call to a member function addEagerConstraints() on boolean.
This is how I call the relationship at the moment
$user = \App\Stack::with(['finished' => function($q){
return $q->where('user_id', auth()->user()->id);
}])->get();
But that returns the whole collection, and that is not necessary. The expected result should be like:
{
"id": 5,
"user_id": 2,
"subject_id": 2,
"name": "tstar igen",
"slug": "tstar-igen",
"description": "asdasd",
"image": null,
"created_at": "2017-10-06 08:27:36",
"updated_at": "2017-10-06 08:27:36",
"finished": true/false
},
So lets say that here is an relation entry in in the progress table for the stack above.
+----+---------+----------+
| id | user_id | stack_id |
+----+---------+----------+
| 1 | 1 | 5 |
+----+---------+----------+
So when fetching the stacks, with relation finish. The finished column should be true or false. So the return from Stack, with relation finished should be like the following. Notice the finished column changed to false on the last stack, because the relation is not present in the progress table.
{
"id": 5,
"user_id": 2,
"subject_id": 2,
"name": "tstar igen",
"slug": "tstar-igen",
"description": "asdasd",
"image": null,
"created_at": "2017-10-06 08:27:36",
"updated_at": "2017-10-06 08:27:36",
"finished": true
},
{
"id": 6,
"user_id": 2,
"subject_id": 2,
"name": "another stack",
"slug": "another-stack",
"description": "This is a test stack for all the stacks out there",
"image": null,
"created_at": "2017-10-06 08:27:36",
"updated_at": "2017-10-06 08:27:36",
"finished": false
},
You can try withCount that counts your related models. But it will return number of related models not boolean. Here is an example:
Stack.php
public function progress(){
return $this->hasOne(Progress::class);
}
then:
\App\Stack::withCount('progress')->get();
Now results have count_progress attribute with number of related 'Progress' models. Because your relation is hasOne it should be 0 or 1.
You are almost done, You have your relationship setup well except for the boolean stuffs, remove it:
public function finished(){
return $this->hasOne(Progress::class);
}
Then when you need to check you can use has or whereHas relationship method:
$stack = \App\Stack::whereHas('finished', function($q){
return $q->where('user_id', auth()->user()->id);
})->get();
Based on your need thats the best I could conceive. I think this should be sufficient.
It would do well to learn how has and whereHas works. But just to summarize: has simply is like whereHas except that it checks if there is at least one model related to the first model, and the other helps you to make more advanced constraints as shown in the example I gave.
Your model
public function progress(){
return $this->hasOne(Progress::class);
}
Your controller
...
$stacks->load('progress');
resource
'finished' => $this->whenLoaded('progress')->isNotEmpty()

couchDB- complex query on a view

I am using cloudantDB and want to query a view which looks like this
function (doc) {
if(doc.name !== undefined){
emit([doc.name, doc.age], doc);
}
what should be the correct way to get a result if I have a list of names(I will be using option 'keys=[]' for it) and a range of age(for which startkey and endkey should be used)
example: I want to get persons having name "john" or "mark" or "joseph" or "santosh" and lie between age limit 20 to 30.
If i go for list of names, query should be keys=["john", ....]
and if I go for age query should use startkey and endkey
I want to do both :)
Thanks
Unfortunately, you can't do so. Using the keys parameter query the documents with the specified key. For example, you can't only send keys=["John","Mark"]&startkey=[null,20]&endkey=[{},30]. This query would only and ONLY return the document having the name John and Mark with a null age.
In your question you specified CouchDB but if you are using Cloudant, index query might be interesting for you.
You could have something like that :
{
"selector": {
"$and": [
{
"name": {
"$in":["Mark","John"]
}
},
{
"year": {
"$gt": 20,
"$lt": 30
}
}
]
},
"fields": [
"name",
"age"
]
}
As for CouchDB, you need to either separate your request (1 request for the age and 1 for the people) or you do the filtering locally.

Find matching array items in MongoDB document

I am developing a web app using Codeigniter and MongoDB.
In the database I got a document that look like this:
{
"_id": {
"$id": "4f609932615a935c18r000000"
},
"basic": {
"name": "The project"
},
"members": [
{
"user_name": "john",
"role": "user",
"created_at": {
"sec": 1331730738,
"usec": 810000
}
},
{
"user_name": "markus",
"role": "user",
"created_at": {
"sec": 1331730738,
"usec": 810000
}
}
]
}
I need to search this document using both user_name and role. Right now when I am using the below code I get both. I only want to get array items matching both user_name and role.
$where = array (
'_id' => new MongoId ($account_id),
'members.user_id' => new MongoId ($user_id),
'members.role' => $role
);
$this -> cimongo -> where ($where) -> count_all_results ('accounts');
This is an old question, but as of MongoDB 2.2 or so you can use the $ positional operator in a projection so that only the matched array element is included in the result.
So you can do something like this:
$this->cimongo->where($where)->select(array('members.$'))->get('accounts');
This is a repeat of this question:
Get particular element from mongoDB array
Also you might want to use $elemMatch
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-ValueinanArray
Here is the rub -- you aren't going to be able to get the array items that match because mongo is going to return the entire document if those elements match. You will have to parse out the code client side. Mongo doesn't have a way to answer, "return only the array that matches."

Resources