Eloquent Relationship on Partial Match - php

Can I have an eloquent relationship where there will only be a partial match on the joins?
To explain, here is one Model;
class PostcodeCoord extends Model {
public function payment()
{
return $this->hasMany('App\Models\Payment', 'Vendor ZIP', 'postcode');
}
}
And my other model;
class Payment extends Model {
public function postcodeCoord()
{
return $this->belongsTo('App\Models\PostcodeCoord', 'postcode', 'postcode');
}
}
My payments table has a column called Vendor ZIP. And my postcode_coords table has a column called postcode.
The problem, is that the postcode column is only 3 or 4 characers long (the first section of a UK postcode Eg SW1A). Whereas, Vendor ZIP, is the full 6 or 7 characters. Eg SW1A2AA.

AFAIK, there is no way to create a relationship on a partial match between the ID fields. That said, I can think of two different ways to solve this:
Add a new column to your payments table called something like postalcodegroup. When the model is created or updated, ensure that this column is filled in with the first part of the postal code. The best way to do this would be via model events. Then you can create your relationship between payments.postcodegroup and postcode_coords.postcode.
Write an accessor on the Payment model which will perform a database query to retrieve the relevant data from the postcode_coords table when it's called.
Of the two, option 1 is probably better (cleaner), although it does require adding columns to your tables.

Related

I don’t know how to get data in laravel correctly by linking the data in this database

In this figure, the remaining tables are linked to the datainfo table. I need to retrieve the entire table data from the datainfo table.
In this picture I have shown the tables themselves
You can create Eloquent Models for each table and define Relationships among them. This is the Correct Laravel Way.
Let's assume Your datainfo table represents your Datainfo model,
Your cars table represents Car model. Same as washtypes and boxes.
then depending on your relation type defined the relation in Datainfo model.
class Datainfo extends Model
{
public function cars()
{
return $this->hasMany(Car::class);
}
}
You also can use hasOne instead of hasMany for one - to one relation
Similarly, create relation defining functions as washtypes() and boxes().
Then To get Datainfo with all related data using something like think in your controller
return Datainfo::with('cars','washtypes','boxes')->get();
Alternatively, you can get the count
return Datainfo::with('cars','washtypes','boxes')->count();
To get a count on a date
return Datainfo::with('cars','washtypes','boxes')->where('created_at',$date_var)->count();
If you only want Datainfo that has relation with cars, washtypes or boxes:
return Datainfo::has('cars','washtypes','boxes')->where('created_at',$date_var)->count();
Use laravel eloquent join clauses, https://laravel.com/docs/8.x/queries#joins
For total amount in same day
You can use eloquent sum and group by methods based on the date

Laravel Eager Loading, multiple same hasOne relations

I have 2 simple models. First, one is called Builds and the second one is called SlotOptions. Each build can have like 5 assigned slots.
class BuildDB extends Model
And has 5 such relations slot1-5 and id changes to slot1-5_id
public function slot1()
{
return $this->hasOne('\App\SlotOptions', 'id', 'slot1_id');
}
In the controller I call it such way;
BuildDB::with([ 'slot1', 'slot2', 'slot3', 'slot4', 'slot5'])->find(5);
\App\SlotOptions model doesn't contain any extra coding.
This generates 5 "same" queries. - atm the eager loading would work if I get a list of builds and each slot would have whereIn clause, is it possible to have it a one big wherein the clause, or does it require to change the DB schema.
It's not possible to optimize eager loading in this case.
I recommend that you change your database schema to a many-to-many relationship.
This design is more flexible, it allows you to easily add more slots in the future.
Create a pivot table named build_slot_option with these columns: build_id, slot_option_id
Add an additional column if you want to number/order the slots.
Then define a BelongsToMany relationship:
class BuildDB extends Model
{
public function slots()
{
return $this->belongsToMany(
SlotOptions::class, 'build_slot_option', 'build_id', 'slot_option_id'
);
}
}
BuildDB::with('slots')->find(5);

Connecting database fields and model attributes in Laravel

I'm just getting going with Laravel, and have used Eloquent to define my Campaign table. I have a Campaign model which is currently empty.
I'm not sure how to add attributes to this model to represent the fields in the db - or even if I should. The Laravel documentation seems thin on models and searches keep leading me to accessors and mutators.
If I have a database field called platform_type in my campaigns table, how do I link the PlatformType model attribute to this field?
To clarify:
This is not a question about relationships - there is only one entity in my solution thus far.
platform_type is a field in my campaigns table because it is an attribute of a campaign - I'm asking how to represent this in my model.
The model has an internal array which stores the attributes of a given row (it's called $attributes and replicated by $original if you look for them in the source code). The reason it's replicated is so when you call save() it will only do a save if you actually changed them from the originals.
You can access said attributes via $modelInstance->getAttribute("platform_type") or $modelInstance->platform_type which will call the magic __get method that in turn calls the getAttribute
So in your case you can have:
$campaign = Campaign::find($id);
echo $campaign->platform_type;
The ORM will automatically create the relevant SQL query and fill the model instance with the attributes of the row it finds.
You need to define relationships. In the PlatformType model:
public function campaigns()
{
return $this->hasMany(Campaign::class, 'platform_type');
}
And in the Campaign model:
public function platformType()
{
return $this->belongsTo(PlatformType::class, 'platform_type');
}
You also need to rename the campaign table to campaigns. Or you should add this to the model to be able to use a custom name:
protected $table = 'campaign';
At this point, these tables will be connected and relationships will work. However, it is recommended to add foreign key constraints.

Is this many-to-many relationship possible in Laravel?

I'm trying to get my head around using polymorphic relationships for a many-to-many relationship between suppliers and products:
products
id
name
suppliers
id
name
product_supplier
id
product_id // belongsToMany easily takes care of this id
supplier_id // and this id
price // this can be fetched using withPivot('price')
deliverymethod_id // I'm having difficulties "joining" this one.
I'm confident in using belongsToMany(), I can easily do something like this:
public function products()
{
return $this
->belongsToMany('Supplier')
->withPivot('price');
}
But the catch here is joining to that third column in the relationship table:
deliverymethods
id
name
I am unsure how to do this. I've been told that Polymorphic Relationships are what I'm after however I'm unsure how to implement them for my situation.
http://laravel.com/docs/4.2/eloquent#many-to-many-polymorphic-relations
According to the documentation, I would have to rename my table columns to include *able_id and *able_type. This is really confusing.
I was expecting laravel to having something like belongsToMany('Supplier')->withAlso('Deliverymethod')
I'm afraid that method does not exist (yet?).
What I fall back to is manually filling in the 3rd relation:
public function products()
{
return $this
->belongsToMany('Supplier')
->withPivot('price', 'delivermethod_id');
}
Now I can access ->pivot->deliverymethod_id on every Product that I get via Supplier.
You could even add a function in your Product model that fills this in automatically:
Class Product ... {
protected $appends = array('deliverymethod');
public function getDeliverymethodAttribute()
{
return Deliverymethod::find($this->pivot->delivermethod_id);
}
Now every time you request a product via it's relation to the supplier, it automatically includes a deliverymethod attribute with the object in it.
(To have it not throw an error when you get a Product directly, just remove the $appends variable from the Product model and call the getDeliverymethodAttribute() method manually whenever you need it.)
Short explanation about polymorphic relations:
Polymorphic relations are for relations, where two models are related to a third model at the same time. So for example both a User and a Product can have a Picture of them. Now, it doesn't make sense to have two models for the pictures (UserPicture and ProductPicture), since they both have the same characteristics. This would be a perfect reason to use a polymorphic relation, where the Picture can both belong to a User or a Product.
However, in your case the Deliverymethod applies directly to the relation between Supplier and Product. So this is not where polymorphic relations would work, but it has instead to be done the way you did it.

Best way to apply Eloquent's ORM with Laravel?

I have two tables:
treatments (
id,
name
)
companies (
id,
name
)
And I need to build a relation to a "price" table. I thougth in something like follows:
prices (
treatment_id,
company_id,
price
)
But i don know how to apply the ORM to a php aplication. I'm using Laravel with Eloguent's ORM. I think that the real question would be if this is a good way to design the db. Perhaps I should make it diferent?
Any advices?
Thanks,
Ban.
If a Company can have multiple Treatments and a treatment can be bought from multiple companies at different prices, then you have a Many-to-many relationship, with prices being the pivot table (which if you would adhere to convention would be named company_treament, but that's not a must). So you'll need to have two models for Treatments and Companies, which would look like this:
class Company extends \Eloquent {
public function treatments()
{
return $this->belongsToMany('Treatment', 'prices')->withPivot('price');
}
and
class Treatment extends \Eloquent {
public function companies()
{
return $this->belongsToMany('Company', 'prices')->withPivot('price');
}
}
The treatments() and companies() methods from the models are responsible for fetching the related items. Usually the hasMany method only requires the related model as the first parameter, but in your case the pivot table name is non-standard and is set to prices by passing it as the second parameter. Also normally for the pivot table only the relation columns would be fetched (treatment_id and company_id) so you need to specify the the extra column using withPivot. So if you want to get the treatments for a company with the id 1 list you whould to something like this:
$treatments = Company::find(1)->treatments;
The opposite is also true:
$companies = Treatment::find(1)->companies;
If you need to access the price for any of those relations you can do it like this:
foreach ($treatments as $treatment)
{
$price = $treatment->pivot->price;
}
You can read more about how to implement relationships using Eloquent in the Laravel Docs.
EDIT
To insert a relation entry in the pivot table you can use attach and to remove one use detach (for more info read the Docs).
$treatment->companies()->attach($companyId, array('price' => $price));
$treatment->companies()->detach($companyId);
To update a pivot table entry use updateExistingPivot:
$treatment->companies()->updateExistingPivot($companyId, array('price' => $price));

Categories