Laravel One to One several columns with same relation - php

I've been tasked with building a website given a database that's already in place, using laravel. I'm not able to make any changes to the database structure, as the system is currently in production.
There are two tables: Item and Transaction. A Transaction has a predefined number of items available for it [Item1...Item8] listed in their own separate columns.
I know I can specify in the model to do a:
function item1() {
$this->hasOne('App\Item', 'item_key', 'item1');
}
For each of the columns within the transaction model.
Is there an easier way to concatenate the items together and give me an array?
Like...
function items() {
$this->hasOne('App\Item', 'item_key', ['item1','item2'....]);
}
Edit:
I am aware I could pull each item individually through the items() method and create the array myself. Just wondering if Laravel provides a helper for it.

Related

Get Laravel collection's item using collection value

Suppose we got a collection from the database like this:
$projects = Projects::all();
Now, for example, I want to get the specific project using the specified column's value. For example, suppose any project has a unique pr_code, Now I want to get an item of collection that pr_code is 1234.
Note = I Know using Projects::where('pr_code', 1234)->first() But I didn't want this. I want use inside collection
How I could do that?
Collections also have where and first (and whereFirst ) functions. So you just need to change the order of functions in your chain: Projects::all()->firstWhere('pr_code', 1234)
Projects::all() queries all records in projects and returns them as a Collection of Projects models, this could be quite a performance hit.
Projects::where('pr_code', 1234)->get() will query for the specific projects and return a Collection of Projects models that have pr_code of 1234.
Projects::where('pr_code', 1234)->first() will do the same, but return the first as a Projects model.
I recommend naming the model Project rather than the plural Projects. The Laravel model is smart enough to know to use the plural name of the database table.

How to compare and update an Eloquent Collection based on another Collection in Laravel

I have a model in Laravel called Player. This player data is pulled from an external API. I am trying to update this external data on a scheduled basis from the external API. The external API data is the authoritative source on what should be in the players table.
What I currently have is two collections, one is the data from the database, the other is the external API data. I constructed new Player models in a collection based on the API data.
What I essentially have right now is this:
Collection $playersInDatabase; // Eloquent Collection of type Player
Collection $playersFromApi; // Collection of type Player
The $playersFromApi data is just the JSON API data converted into new Player models and added to the collection.
My problem is that I can't just wipe the whole players table as I am only modifying a subset of the table at a time. Is there an effective way to use Laravel to compare these two? I want to add any new Player models to the database that don't exist, update any existing Player model that has differing data, and then also delete any records that the API data no longer has but are still in the database (stale records).
The only way I can think of to do this involves iterating over the collections multiple times to accomplish what I want to do and I feel like there is an easier more elegant way to do this that better utilizing the framework.
For reference here is what the players table looks like. I am currently using seeder data:
You could do something like this. No comparisons necessary, just updateOrCreate() and delete any ids not updated for the corresponding faction in a single DB call.
// the faction id you are querying from API
$faction_id = ...;
// for storing updated model ids
$updated_ids = [];
foreach ($playersFromApi as $playerFromApi) {
// update record or create a new one
$player = Player::updateOrCreate(
[
// conditions to meet
'...' => $playerFromApi['...']
],
[
// data to update
'...' => $playerFromApi['...']
]
);
// store id of updated model
$updated_ids[] = $player->id;
}
// delete models not updated
Player::where('faction_id',$faction_id)->whereNotIn('id',$updated_ids)->delete();

Get a collection of relationship items through multiple relations in Laravel

I have the following relationships:
Users have polymorphic many to many (morphedByMany) relationships with Customers, Locations, and Vendors.
Customers, Locations, and Vendors each have hasMany or hasManyThrough relationships with Datasets.
I'd like to get the Datasets that a given User has access to via its relationships. Also, some of the datasets through the different relationships might be the same, so I want a unique list.
I created the following method on my User model that works correctly (inspired by Laravel get a collection of relationship items):
public function accessibleDatasets()
{
$datasetsByCustomers = $this->customers()->with('datasets')->get()->pluck('datasets')->flatten();
$datasetsByLocations = $this->locations()->with('datasets')->get()->pluck('datasets')->flatten();
$datasetsByVendors = $this->vendors()->with('datasets')->get()->pluck('datasets')->flatten();
$datasets = $datasetsByCustomers;
$datasets = $datasets->merge($datasetsByLocations);
$datasets = $datasets->merge($datasetsByVendors);
return $datasets->unique();
}
Is there a "right way" or more efficient way to get this (besides using some sort of a reduce function for the merges)? Is loading the models, then flattening better? The associated values aren't going to change too often, so I can cache the results, but wanted to get some feedback.
The most efficient way would be by performing a single database query as Tim already mentioned.
Since you can cache the results and should you want to spare some lines of code I'd rather do:
public function accessibleDatasets()
{
$this->load('customers.datasets', 'locations.datasets', 'vendors.datasets');
return $this->customers->flatMap->datasets
->merge($this->locations->flatMap->datasets)
->merge($this->vendors->flatMap->datasets);
}
The merging operation should take care of duplicate keys but feel free to add a unique call if it doesn't.

Concept of table relations in Laravel

I have two tables:
Cards
Notes
Each Card has multiple Notes. So there is a relation between them like this:
class Card extends Model {
public function notes ()
{
return $this->hasMany(Note::class);
}
}
Ok well, all fine.
Now I need to understand the concept of these two lines:
$card()->$notes()->first();
and
$card()->$notes->first();
What's the difference between them? As you see in the first one $note() is a function and in the second one $note isn't a function. How will they be translated in PHP?
The first one points out to the card table and the second one points out to the notes table, right? or what? Anyway I've stuck to understand the concept of tham.
I don't know about $ before the $notes in your code but if you trying to say something like this.
1- $card->notes()->first();
2- $card->notes->first();
In the code in line 1, first you have a $card model and then you wanted to access all notes() related to that $card, and because of adding () after notes you simply call query builder on notes, show you can perform any other database query function after that, something like where, orderBy, groupBy, ... and any other complicated query on database.
But in the second one you actually get access to a collection of notes related to that $card, we can say that you get all related notes from database and set it into laravel collections and you are no more able to perform database query on notes.
Note: because laravel collections have some methods like where(), groupBy(), whereIn(), sort(), ... you can use them on the second one, but in that case you perform those methods on collections and not database, you already get all results from database

Doctrine 2 mapping process manipulation

I want to ask if its clean possible to change the mapping process.
For example, if i have an database, with products and offers, so the relation is 1 product n offers.
I want to get on fetch an product with multiple offers not an collection of offers, instead of i want to get an class named "AggregateOffers" which calculates the highest the lowest price and includes the offers collection.
Did anyone know an good/clean solution?
For an better understanding, schema.org defines the relation between an product an multiple offers, within an aggregateoffer, so i want to abstract them, to generate at the end an json-ld.
My actually design looks like schema, as you can see i have an 1-n relation from Product to Offer, what i actually want to get when i read from the DB, is an extended relation.
For example:
Product -> getOffers (should not return an collection of offers, instead it should return an "AggregateOffers" which hold the collection of offers).
The only idea, is to update the "getOffers" function and return die Aggregation class, but this acts not really good on me.

Categories