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.
Related
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.
Tried merging two JSON responses into a single one but the problem is the data is displayed in two arrays and I want it in a single array. How do I achieve it in Lumen/Laravel
Tried contatinating two arrays or responses
public function index(Request $request)
{
$post = Post::orderBy('id', 'DESC')->get();
$post_images = PostImage::orderBy('id', 'DESC')->get();
return $this->successResponse($posts.$post_image );
}
Expected:-
{
"post": {
"id": 14,
"post_id": 798965728,
"user_id": 1,
"location": "first",
"title": "un8852",
"cooked_time": "1554329910",
"dispose_time": "1554373110",
"food_type": "nv",
"description": "asdfg",
"serve_quantity": 23,
"lat": 19.08,
"lon": 73,
"id": 10,
"post_id": 798965728,
"image_name1": null,
"image_name2": null,
"image_name3": null,
"image_name4": null,
"image_name5": null,
},
"st": "1",
"msg": "success"
}
Got:-
{
"post":"{\"id\":14,\"post_id\":798965728,\"user_id\":1,\"location\":\"first\",\"title\":\"un8852\",\"cooked_time\":\"1554329910\",\"dispose_time\":\"1554373110\",\"food_type\":\"nv\",\"description\":\"asdfg\",\"serve_quantity\":23,\"lat\":19.08,\"lon\":73,\"created_at\":\"2019-04-04 10:20:18\",\"updated_at\":\"2019-04-04 10:20:18\"}{\"id\":10,\"post_id\":798965728,\"image_name1\":null,\"image_name2\":null,\"image_name3\":null,\"image_name4\":null,\"image_name5\":null,\"created_at\":\"2019-04-04 10:20:18\",\"updated_at\":\"2019-04-04 10:20:18\"}",
"st":"1",
"msg":"success"
}
There are some missing pieces there, but I think I see what's happening.
Based on the result you're getting, it looks like $posts and $post_image in this code are eloquent models.
return $this->successResponse($posts.$post_image );
When you concatenate them, their __toString() methods convert them to strings, which is done using the toJson() method. So basically you have two JSON objects stuck together, which isn't valid JSON, and then the successResponse() method encodes them again.
To merge them, you can convert them to arrays, merge those, then pass the result to successResponse().
$merged = array_merge($posts->toArray(), $post_image->toArray());
return $this->successResponse($merged);
The result you want is impossible, though. The "post" object has two different values of "id". You'll only be able to get one. If you use
$merged = array_merge($posts->toArray(), $post_image->toArray());
Then the id value of the second object will replace the first one. If you want to keep the first id value, you need to use union instead of array_merge.
$merged = $a->toArray() + $b->toArray();
You can definitely concatenate two JSON arrays, You have to parse the objects and concatenate them and re stringify.
This question might be answered here also. https://stackoverflow.com/a/10384890/1684254
I think you can't concatenate 2 JSON objects as strings.
The proper way would be to:
Get the Post and the PostImage objects
$post = Post::orderBy('id', 'DESC')->get();
$post_images = PostImage::orderBy('id', 'DESC')->get();
Serialize the Post object
https://laravel.com/docs/5.8/eloquent-serialization
Use the method described below to add each field of the PostImage object (image_name1 .. image_name5) to the JSON
How to add attribute in JSON in PHP?
Return the JSON
Update:
Post::orderBy('id','DESC')->get()-> first() returns one object.
Post::orderBy('id','DESC')->get() returns a collection of objects and it would require a different approach.
Try this:
$post = Post::orderBy('id', 'DESC')->get();
$post->map(function ($item, $key) {
$post_images = PostImage::where('post_id', $item->id)->get()->first();
$item->setAttribute('image_name1',$post_images->image_name1);
$item->setAttribute('image_name2',$post_images->image_name2);
$item->setAttribute('image_name3',$post_images->image_name3);
$item->setAttribute('image_name4',$post_images->image_name4);
$item->setAttribute('image_name5',$post_images->image_name5);
return $item;
});
return $this->successResponse($posts);
In my emails table, I have a column named To with column-type Json. This is how values are stored:
[
{
"emailAddress": {
"name": "Test",
"address": "test#example.com"
}
},
{
"emailAddress": {
"name": "Test 2",
"address": "test2#example.com"
}
}
]
Now I want a collection of all emails sent to "test#example.com". I tried:
DB::table('emails')->whereJsonContains('to->emailAddress->address', 'test#example.com')->get();
(see https://laravel.com/docs/5.7/queries#json-where-clauses)
but I do not get a match. Is there a better way to search using Laravel (Eloquent)?
In the debugbar, I can see that this query is "translated" as:
select * from `emails` where json_contains(`to`->'$."emailAddress"."address"', '\"test#example.com\"'))
The arrow operator doesn't work in arrays. Use this instead:
DB::table('emails')
->whereJsonContains('to', [['emailAddress' => ['address' => 'test#example.com']]])
->get()
I haven't used the json column but as the documentation refers, the below code should work fine.
DB::table('emails')
->where('to->emailAddresss->address','test#example.com')
->get();
In case to store array in json format. And just have an array list of IDs, I did this.
items is the column name and $item_id is the term I search for
// $item_id = 2
// items = '["2","7","14","1"]'
$menus = Menu::whereJsonContains('items', $item_id)->get();
Checkout the Laravel API docs for the whereJsonContains method
https://laravel.com/api/8.x/Illuminate/Database/Query/Builder.html#method_whereJsonContains
Using Eloquent => Email::where('to->emailAddress->address','test#example.com')->get();
You can use where clause with like condition
DB::table('emails')->where('To','like','%test#example.com%')->get();
Alternatively, if you have Model mapped to emails table names as Email using Eloquent
Email::where('To','like','%test#example.com%')->get();
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);
I refered to Wordpress' database to create config setting for my app in Laravel. I created a app_settings table with id, setting_name and setting_value as columns to store each config name and value in rows. It works fine but the problem occurs whenever I retrieve or update data.
$appsetting = new AppSetting();
return $appsetting->all();
When I query the database it returns me a json like:
[
{
"id": 37,
"setting_name": "site_name",
"setting_value": "Title",
"created_at": "2015-03-09 10:40:35",
"updated_at": "2015-03-11 03:23:48"
},
{
"id": 38,
"setting_name": "site_url",
"setting_value": "http://localhost:800",
"created_at": "2015-03-09 10:40:35",
"updated_at": "2015-03-11 03:23:48"
},
{
"id": 39,
"setting_name": "site_admin",
"setting_value": "local#host.com",
"created_at": "2015-03-09 10:40:35",
"updated_at": "2015-03-11 03:23:48"
}
]
Case 1:
Whenever I had to use a variable I had to remember the column index to get it's value. Eg. $settings[0]['setting_value'] to retrieve Titleand this make code pretty much static than using something like $settings['site_name'].
Case 2:
If I had to update multiple settings at once, I had to use multiple update commands with where clause.
$appsetting::where('setting_name', '=', 'site_name')->update(['setting_value' => $setting['title']]);
$appsetting::where('setting_name', '=', 'site_url')->update(['setting_value' => $setting['url']]);
$appsetting::where('setting_name', '=', 'site_admin')->update(['setting_value' => $setting['email']]);
What are the best way to update or retrieve rows in database in this case?
I want output like this while using attributes like site_title in rows
[{
"site_title" : "Title",
"site_url" : "http://localhost",
"site_admin" : "local#host"
}]
You have encountered the fundamental problem with using the EAV database structure (although yours may more acurately be describes as Row Modelling) http://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model
This type of data structure is never easy to query or update and I would recommend that you drop it and just create a config table with columns for each config var. At some point you are going to have defined all the settings that you need so you can build the table for them. If you later want to add another config setting then just add the column to the table.
That said, if you insist on using the EAV then have your code convert the results into something more usable:
$config = new \stdClass;
foreach($result as $row){
$config->$row['setting_name'] = $row['setting_value'];
}
$title = $config->site_title;
$output = array();
foreach ($settings as $setting) {
$output[] = array($setting['setting_name'] => $setting['setting_value']);
}
return json_encode($output);
I Hope this sample will help you, but i wrote the code in Doctrine DQL:
$query=$this->em->createQuery('UPDATE Alireza\Domain\Entities\Pages_modules u
SET u.Sorder =
WHEN u.R THEN 'Road'
WHEN u.M THEN 'Mountain'
WHEN u.T THEN 'Touring'
WHEN u.S THEN 'Other sale items'
ELSE 'Not for sale'
WHERE u.id IN(1,2,8,9)');
Return $query->getResult ();
this type of queries do not have n+1 problem