Laravel's nested relation first element - php

Okay, so after much googling... I have come across a unique case for Laravel.
I want to obtain the first element of the collection (not an array) and then with the nested relation also obtain the first element.
For example:
auth()->user()->marks()->with('subjects', function($q) {
$q->first();
})->first();
returns (note that subjects is an array but the code states first)
{
"id": 1,
"name": "Ahsan",
"mark": 369,
...
"subjects": [{...}]
}
Instead the query should have returned (b/c $q->first() returns the first element):
{
"id": 1,
"name": "Ahsan",
"mark": 369,
...
"subjects": {...}
}
Qq: How can I modify the query to get the first subject without complicating the query? Am I missing something crucial here regarding with and first?
Aside:
Marks is One-to-Many relation. 369 can return multiple subjects if needed, but since I require the first it should be an element not an array of single element.

Related

How to search through unknown property name in database with Laravel

I'm trying to search database with json contains method of laravel. Here is my JSON of one database line:
{
"row": {
"1": {
"ID":"110555175667"
},
"2": {
"ID":"11023235667"
},
"3": {
"ID":"11001414141667"
},
"4": {
"ID":"11023235667"
},
"5": {
"ID":"1100012222225667"
},
}
}
I want to search ID, but as you see there are numbers as properties.
In example I want to find 11023235667. I've tried it like that:
->whereJsonContains('json', [['row' => ['1' => ['ID' => '11023235667']]]])
But it didn't worked. How can I do it?
EDIT:
I have also tried this:
->whereRaw('JSON_CONTAINS(json, "$.row.*.ID", "11023235667")')
I know the property of row must be JSON array to accomplish to match the id, but it has been set as JSON object
The usage of JSON_CONTAINS() accepts a JSON document as its second argument, not a path.
You could use that path to extract the ID's into an array:
SELECT JSON_EXTRACT(json, '$.row.*.ID') FROM ...
+--------------------------------------------------------------------------------------+
| ["110555175667", "11023235667", "11001414141667", "11023235667", "1100012222225667"] |
+--------------------------------------------------------------------------------------+
Using this, you can search the resulting array:
SELECT ... FROM mytable
WHERE JSON_SEARCH(JSON_EXTRACT(json, '$.row.*.ID'), 'one', '11023235667') IS NOT NULL;
You would need to do this using whereRaw() in Laravel, because Laravel doesn't have a builtin query builder function for this expression.
Tip: As soon as you reference a JSON column in the WHERE clause of an SQL query, your query becomes harder to write, and harder to optimize. This should be a red flag indicating your design is wrong. You would be better off storing data in normal rows and columns, not JSON.

Laravel sortBy() includes keys in the output, but sortByDesc doesn't?

I'm fetching a collection with relationship and then I try to sort by a column in one of the relationships. The output for using sortBy() is like this:
{
"1": {
"id": 1,
},
"0": {
"id": 2,
}
}
However, when I use sortByDesc() it comes out like this:
[
{
"id": 2,
},
{
"id": 1,
}
]
Is there a reason for this? It doesn't present a problem if I use it inside a Controller or View, however it is used as output in an AJAX call and breaks everything. Is there a way to have consistent output? sortByDesc() output works best for me since I don't need keys.
sortBy() and sortByDesc() behave the same way, the difference is your data.
If the sorted result has consecutive integer keys (0, 1, 2), json_encode() will return an array (your second case). Otherwise, json_encode() will return an object (your first case).
you may use something like this
$collections->sortBy(function ($collection) {
return $collection->id;
})->values();

Load specific relations in a nested eager loading in laravel

I have the following related tables:
tableA
- id
- value
tableB
- id
- tableA_id
- value
tableC
- id
- tableB_id
- value
tableD
- id
- tableC_id
- value
I normally use a nested eager loading to get the object of tableaA from tableD, for example:
$table_d = TableD::with('TableC.TableB.TableA')->find($id);
And I get an object like this:
{
"id": 1,
"value": "value",
"tableC_id": 1,
"tablec": {
"id": 1,
"value": "value",
"tableB_id": 1,
"tableb": {
"id": 1,
"value": "value",
"tableA_id": 1,
"tablea": {
"id": 1,
"value": "value"
}
}
}
}
What I want to achieve is to obtain only the object of table D, with its object from table A related, without having table C and table B in the final object, something like this:
{
"id": 1,
"value": "value",
"tablea": {
"id": 1,
"value": "value"
}
}
}
I tried adding this function in the model file of Table D:
public function TableA()
{
return $this->belongsTo('App\Models\TableC', 'tableC_id')
->join('tableB','tableC.tableB_id','=','tableB.id')
->join('tableA','tableB.tableA_id','=','tableA.id')
->select('tableA.id', 'tableA.value');
}
but it does not work because when I do the following query, it returns some good objects and others with tableA = null:
$tables_d = TableD::with('TableA')->get()
Am I doing something wrong or is there another way to achieve what I want?
You may be able to skip a table with
this->hasManyThrough() but depending on what you really want as 'future features', you may want to have multiple relations with whatever code you desire according to your needs. QueryScopes aswell.
One can generally use a has many through relationship for mapping tables when it is just two tables and a linking table between. You have yet another join beyond that so it won't really be much better than what you have currently.
Have you considered another mapping table from D to A directly or a bit of denormalization? If you always need to load it like that you might benefit from having a bit of duplicated fks to save on the joins.
This will really depend on your needs and it is not 3NF (third normal form), maybe it's not even 2NF, but that's why denormalization is like comma use...follow the rules generally but break them for specific reasons; in this case to reduce the number of required joins by duplicating a FK reference in a table.
https://laravel.com/docs/5.6/eloquent-relationships#has-many-through
You can try to do this:
- add a method in TableD Model:
public function table_a()
{
return $this->TableC->TableB->TableA();
}
then use: TableD::with(table_a);

Join table fields as a single JSON object

I am building API. I ran into issue when building responses such as this one:
{
"id": 1,
"name": "Some name",
"my_joined_table": {
"joined_table_id": "10",
"some_joined_table_field": "some value"
}
},
Joining tables as described in https://laravel.com/docs/5.2/queries would yield result such as:
{
"id": 1,
"name": "Some name",
"joined_table_id": "10",
"some_joined_table_field": "some value"
},
Instead of using join I could just run two queries, one for main table, second one for secondary and then just append second array to first one and spit JSON response, but it's a lot of queries and appending if list is big!
Example code which yields second result in pseudo-code:
$data = Model::select('id', 'name', 'my_joined_table.id as joined_table_id', 'my_joined_table.some_value some_value')
->leftJoin('my_joined_table', function($join) { //conditions_callback
})->get();
return response()->json($data);
Please advice.
EDIT2:
It seems that I can use with as follows:
$data = Model::with('my_second_table')->first();
return response()->json($data);
It does what I want, only the problem, that I cannot specify fields for both first and second tables using ->first($fields) and->with(['my_second_table' => function ($query) { $query->select('id', 'some_value'); }]) unless I specify primary key of second table in ->first($fields). How do I work around this?
TL;DR; Issue: http://laravel.io/bin/YyVjd
You can probably use Laravel Eloquent relationship to achieve it.
https://laravel.com/docs/5.2/eloquent-relationships#one-to-many
Or you can remap the returned data to a new response object using $appends.
Try something here,
http://laraveldaily.com/why-use-appends-with-accessors-in-eloquent/
This is just some clues and there is a lots work to do.
FYI, you can set $visible in your model to specify which attributes is visible.

Targeting object within array JSON Steam API

How do I access webm->max in this Steam API? It's the order [{ that confuses me, array of one before object? I'm not quite sure about the targeting here..
I've tried:
$gameTrailer = $game_json->57690->data->movies[0]->webm->max;
and
$gameTrailer = $game_json['57690']['data']['movies']['webm']['max'];
The API text is like this:
"movies": [{
"id": 2029441,
"name": "Tropico 4 Gameplay Trailer",
"thumbnail": "http:\/\/cdn.akamai.steamstatic.com\/steam\/apps\/2029441\/movie.293x165.jpg?t=1447358847",
"webm": {
"480": "http:\/\/cdn.akamai.steamstatic.com\/steam\/apps\/2029441\/movie480.webm?t=1447358847",
"max": "http:\/\/cdn.akamai.steamstatic.com\/steam\/apps\/2029441\/movie_max.webm?t=1447358847"
},
"highlight": true
}],
and "movies" lies within:
{"57690": {
"data": {
Assume I'll always want the very first movie in an array (which in this case is an array of one). Thanks in advance.
Correct syntax:
$game_json->{57690}->data->movies[0]->webm->max
When you have an object with a numeric key, you have to wrap the key name by curly brackets (Numeric keys are not valid property names).
If you use the associative option:
json_decode( $data, True );
your second attempt is almost right. Simply add the correct index after movie:
$gameTrailer = $game_json['57690']['data']['movies'][0]['webm']['max'];

Categories