Load specific relations in a nested eager loading in laravel - php

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);

Related

Laravel's nested relation first element

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.

How to get array in array with laravel and mysql database?

I am using Laravel (PHP) and MySQL for my backend. I am creating methods for setting and getting information from the database. Those information are being send as a json to the frontend.
I can send table information like:
[
{
"id": 4,
"name": "Max"
},
{
"id": 5,
"name": "Eric"
}
]
For this I am using laravel methods like: DB::table('user')->select('user.id', 'user.name')->get();
However my friend who is doing the frontend want the following json code:
[
{
"id": 4,
"name": "Max"
"specific_user_price":{
"S":5.00,
"M":6.00,
"XL":8.00
}
},
{
"id": 5,
"name": "Eric"
"specific_user_price":{
"S":5.50,
"M":10.00,
"XL":15.00
}
}
]
"specific_user_price is a table and "S", "M", "XL" are columns which have different values depending on the user. I do not know how I can create specific_user_price as an array in a query. I can use group_concat but he needs the json like displayed above.
My idea was to create additional columns in user "size S price", "size M price" and "size XL price". However my friend want those values as an own array group, because some users only habe access to one size, so he would get null values.
Any ideas which method in PHP or Laravel I can use for that? Or is there a MySQL method for creating such thing in a query?
Firstly use Models, way easier to work with out of the box. Define your User model like this, with a relationship for the price.
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function specificUserPrice() {
return $this->hasOne(UserPrice::class);
}
}
You also need to have the UserPrice model defined.
use Illuminate\Database\Eloquent\Model;
class SpecificUserPrice extends Model
{
}
Laravel automatically transforms models, you can get away with the following code in the controller.
public function index()
{
return User::with('specificUserPrice')->get();
}
DB::table('user')->select(['id', 4],['name','max'])->get();
DB::table('user')[0]->get()
This gets you the first element in the array

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.

What is the best way to update or retrieve rows in database?

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

Can I query twice to get more specific value in mongoDB?

For an example, there is a group called "A" which is an array.
And there is another group called "B" which is inside of group "A" also an array.
I want to find and update group "B" elements.
I tried to query chain-like query like in jQuery.
db.collection.findOne({"group":"A"}).findOne({"society":"B"})
something like this..
but this does not work. But main point is that I want to query group elements in group.
Any suggestion on doing this?
If you give me advice especially with PHP implementation, it will be really helpful
Maybe I am misunderstanding something, but what is wrong with:
db.collection.findOne({"group":"A", "society":"B"})
Also note that findOne only returns one document.
Assuming your document looks something like this:
db.mycol.insert( {
"_id": 1,
"group": "A",
"societies": [
{"society": "A", "name": "Alpha" },
{"society": "B", "name": "Beta" }
]
} );
Then in the Mongo shell, you can retrieve the document you want using a query:
var group = db.mycol.findOne( { "group": "A" } );
And then further filter down on its fields using some client-side JavaScript:
var societyB = group.societies.filter(function (val) {
return (val.society == "B");
} );
printjson(societyB);
You'd be able to do something similar with the PHP driver. The key is to perform the action in separate steps: first grab the document you're interested in; then filter and manipulate its fields; then save it back to the database.

Categories