Laravel model data foreach - php

I have the following line $totalRequests = \App\LoanRequest::all(); which returns all records, Along a model Applicant which have One to Many relation with LoanRequest.
So i can access applicants data with $totalRequests[0]->applicant->region for with foreach().
Question :
Now i want all the distinct regions in the Applicant or table applicants from the LoanRequest::all() model and want to push those regions to an array which then i can pass to a view.
What i am trying :
$totalRequests = \App\LoanRequest::all();
foreach($totalRequests as $applicant){
$filterMeta = [];
array_push($applicant->aplicant->region, $filterMeta);
}
It throws:
Indirect modification of overloaded property App\Applicant::$region has no effect

If you want to push the region to the array, you're using the wrong argument order. Also, shouldn't you be initalising the filter meta array outside of the loop?
$totalRequests = \App\LoanRequest::all();
$filterMeta = [];
foreach($totalRequests as $applicant){
array_push($filterMeta, $applicant->aplicant->region);
}

Related

Laravel. After call toArray(), the value in attribute property is changed

I want to get address data from customer table.
But my address data includes country, city and address.
Therefore I want to make address field to country, city and address three fields.
The following is my code
$result = Customer::select('customers.address AS country', 'customers.address AS city', 'customers.address')->get();
dump($result[0]); // First result
$temp = $result->toArray();
dd($temp[0]); // Second result
The following is my questions:
Why is country and city changed in attribute property? I didn't do anything.
It is magic. I change [country] to [countrys] and [city] to [citys].
The following is my new code
$result = Customer::select('customers.address AS countrys', 'customers.address AS citys', 'customers.address')->get();
dump($result[0]);
$temp = $result->toArray();
dd($temp[0]);
The countrys and citys didn't change in attribute property anymore.
The output picture:
Can anyone tall me why? It's really strange.
As per Laravel Documenation:
The toArray method converts the collection into a plain PHP array. If the collection's values are Eloquent models, the models will also be converted to arrays
Aslo
toArray also converts all of the collection's nested objects that are
an instance of Arrayable to an array. If you want to get the raw
underlying array, use the all method instead.
Laravel -> Collection -> toArray()
The following is my code to resolve my question [Why is country and city changed in attribute property?]
// Add the following code to Customer model
public function setCountryAttribute($value)
{
$this->attributes['country'] = $value;
}
public function getCountryAttribute()
{
return $this->attributes['country'];
}
public function setCityAttribute($value)
{
$this->attributes['city'] = $value;
}
public function getCityAttribute()
{
return $this->attributes['city'];
}

Dynamic accessing of the php list

I have 2 tables in the database employee(Ename,id,manager_id) and manager(name,id).1 manager has multiple employees under him.
The first line extracts all the employees under 1 manager and creates a list of it. Now, I wish to extract the name of each employee name from the retrieved employee id. How do i access each element of the list? This is what i have tried and it throws errors
$emplyeeId=DB::table('employee')->where('manager_id', $givenManagerId)->lists('id');
for ($i=0;$i<listCount;$i++)
{
$Ename = DB::table('employee')->where('id', $emplyeeId($i))-> value('Ename');
}
By looking at the docs (and scroll down a little bit, you find a method named pluck. This will return an array with all the values of the given column.
In your case this would be:
$names = DB::table('employee')->where('manager_id', $givenManagerId)->pluck('Ename');
// This will return the following array:
['Kevin', 'Tom', 'Tina', ...]
Laravel pluck method ,for example get just name from data
$names = DB::table('employee')->where('manager_id', $givenManagerId)->pluck('Ename');
Second method with array_filter
$names = DB::table('employee')->where('manager_id', $givenManagerId)->get()->toArray();
$justNames=array_filter($names, function ($ar) { return $ar['Ename']; });

Can we add custom values to a CakePHP Table Object?

I have a Cake Object when querying a table:
$invoices = TableRegistry::get('invoices')->find('all', ['conditions' => ['order_number =' => $orderNumber]]);
This works fine. I then want to add other array key/values to this Object, like this one:
$invoicesTmp = array();
$invoicesTmp['customer'] = "name of customer";
But $invoicesTmp is incompatible with $invoices. (one is an array, other is an CakePHP Object)
I have tried this:
compact($invoices, $invoicesTmp);
but that didn't worked.
The find() method of a Table object returns a Cake\ORM\Query object. This object is used to build SQL queries and to execute them. It has some features to define how the results from the query should be returned.
When CakePHP fetches results from the database the records are stored as an array, and CakePHP then converts them to Entity objects. A process called "hydration" of entities. If you disable hydration the records are returned as just an array.
$query = TableRegistry::get('invoices')
->find()
->where(['order_number'=>$orderNumber])
->enableHydration(false);
foreach($query as $record) {
pr($record);
}
The above creates a query object, and you can iterate over the query records because the object itself supports iteration.
The query object implements the Cake\Collection\CollectionInterface interface, which means we can perform a bunch of collection methods on it. The most common method is the toArray().
$invoices = TableRegistry::get('invoices')
->find()
->where(['order_number'=>$orderNumber])
->enableHydration(false)
->toArray();
The $invoices variable is now a valid array object holding the all the records with each record as an array object.
You can now easily use array_merge to assign extra metadata to each record.
$invoices = array_map(function($invoice) {
return array_merge(['customer'=>'name of customer'], $invoice);
}, $invoices);
$this-set(compact('invoices'));
Updated:
Based upon the comments it appears you wish to use two different tables with different column names, but those columns represent the same data.
Field Aliases
You can rename fields in the SQL query to share a common alias.
$table = TableRegistry::get($whichTable ? 'table_a' : 'table_b');
$records = $table->find()
->select([
'id',
'invoice_id',
'name' => ? $whichTable ? 'customer_name' : 'invoice_name'
])->all();
The above selects a different column for name depending upon which table is being used. This allows you to always use $record->name in your view no matter which table.
I don't like this approach, because it makes the source code of the view file appear to reference a property of the entity that doesn't really exist. You might get confused when returning to the code later.
Field Mapping
From a MVC perspective. Only the controller knows what a view needs. So it's easier if you express this knowledge as a mapping.
$map = [
'id'=>'id',
'invoice_id'=>'invoice_id',
'name' => ? $whichTable ? 'customer_name' : 'invoice_name'
];
$table = TableRegistry::get($whichTable ? 'table_a' : 'table_b');
$records = $table->find()
->select(array_values($map))
->all();
$this->set(compact('records','map'));
Later in your view to output the columns you do it like this:
foreach($records as $record) {
echo $record->get($map['name']);
}
It becomes verbose as to what is happening, and why. You can see in the view that the controller provided a mapping between something called name and the actual field. You also know that the $map variable was injected by the controller. You now know where to go to change it.

Assign object to object/array in PHP

Long time reader, first time asker. I'm experienced with things like Java/C but PHP is new to me.
I'm having an issue where an assignment doesn't assign to where I'd expect it to.
I'm getting an array from a MySQL database via Eloquent methods, in particular:
$result= TableA::where('tableA.id', '=', $id)
->with('tableB.tableC')
->get();
For reference, printing $result out looks like this:
[{"id":105, /*TableA fields*/, "tableB":null},
{"id":106, /*TableA fields*/, "tableB":null},
{"id":107, /*TableA fields*/, "tableB":{/*tableB fields*/, "tableC":
{"id":104, /*TableC fields*/}}},
{"id":108, /*TableA fields*/, "tableB":{/*tableB fields*/, "tableC":
{"id":105, /*TableC fields*/}}}]
In some cases a TableA tuple will have an associated record in tableB and hence tableC, other times there isn't an associated record in tableB. If there isn't an associated record I want to go through and make a temporary "dummy" record to pass through instead of passing through null. The code I'm using to do so is:
for ($i=0; $i < count($result); $i++)
{
if($result[$i]["tableB"] == null)
{
Log:info($result); //Print line A
$result[$i]["tableB"] = OtherController::makeDummyTableB(); //Assignment line
Log::info($result); //Print line B
Log::info($result[$i]["tableB"]); //Print line C
}
}
The problem is that the assignment line doesn't assign to the "tableB" field in the object/array returned in $result. Printing $result out at print line A and B gives the same result, with "tableB" being null for the first two records. Print line C however gives the output I'm expecting, which is the dummy record I'm creating
{"tableC":{/*TableC fields*/}}
So the assignment is doing something, but it's not assigning to the field in $result that's already there, and instead is assigning it to somewhere else (That doesn't show up when I attempt to display it)
If anyone could let me know what my current code is actually doing, and how to have it do what I'm expecting (replace "tableB":null with "tableB":{"tableC":{/*TableC fields*/}} ) it'd be much appreciated
Eloquent models have a lot of "magic" going on in the background. The fields from the table are loaded into an attributes property, and the relationships are loaded into a relations property.
The issue you're running into is that tableB is a relationship field, not a table field. Its data is stored in the relations property. The relations property, however, is not directly modifiable the way you are attempting. When you attempt to modify it using $result[$i]["tableB"], that code is actually modifying the tableB field in the attributes property.
Then there is the issue of getting the data. When you attempt to read the data directly using $result[$i]["tableB"], it will first look in the attributes property, and if it isn't found there, then it will look in the relations property.
However, when you dump the entire object using Log::info($result);, any data in the relations property overwrites the data in the attributes property. So, after a direct assignment using $result[$i]["tableB"] = 'asdf', Log::info($result) will not show the change (since relations overwrites attributes), whereas Log::info($result[$i]["tableB"]) will show the change (since it looks at attributes first).
So, analyzing your code, we have:
for ($i=0; $i < count($result); $i++) {
if($result[$i]["tableB"] == null) {
// At this point:
// - tableB relation is null
// - tableB attribute does not exist
// This is a full dump, so the relations overwrites the attributes.
// tableB will show null
Log:info($result);
// After this assignment executes:
// - tableB relation will be null
// - tableB attribute will be the dummy object
$result[$i]["tableB"] = OtherController::makeDummyTableB();
// This is a full dump, so relations (null) overwrites the attributes (dummy object).
// tableB will show null
Log::info($result); //Print line B
// This is direct access, which accesses attributes (dummy object) before relations (null).
// tableB will show dummy object
Log::info($result[$i]["tableB"]); //Print line C
}
}
If you really want to go about it this way, you should use the setRelation() method:
$result[$i]->setRelation('tableB', OtherController::makeDummyTableB());
That will specifically set tableB on the relations property, which is what you're trying to do. That should get everything working for you.
Having said that, you may be able to tackle this a different way. If you're using Laravel >= 5.3 and your tableB relationship is a HasOne (5.3+) or a BelongsTo (5.4+) relationship, you can use the withDefault() functionality on the relationship so that it will automatically generate a default model when one does not exist in the database. You can read more about this in the documentation here.
So, your relationship definition would look something like:
public function tableB()
{
return $this->hasOne('App\TableB')->withDefault();
}
Now, when no tableB record exists, the relationship will load with a new empty TableB object, instead of null.
If you need something more than just an empty TableB object, you can pass a function to the withDefault() method, and that function will be used to generate the default object.
Eloquent does not return a plain PHP array, when using the get method it will return an instance of Illuminate\Support\Collection and you cannot simply assign an object like that into a Collection. To do so, you have to convert the collection to an array first:
$result = TableA::where('tableA.id', '=', $id)
->with('tableB.tableC')
->get();
// Convert collection to array
$result = $result->toArray();
for ($i=0; $i < count($result); $i++)
{
if($result[$i]["tableB"] == null)
{
Log:info($result); //Print line A
$result[$i]["tableB"] = OtherController::makeDummyTableB(); //Assignment line
Log::info($result); //Print line B
Log::info($result[$i]["tableB"]); //Print line C
}
}
Now you should have your value correctly assigned.
Try
foreach ($result as $whatever)
{
if($whatever->tableB == null)
{
$whatever->tableB = OtherController::makeDummyTableB();
}
}
The problem is that you can't assign a tableB object to a array field of your tableA model...
Your tableB model most likely (I assume, depends how you built it) is related to your tableA model via a "tableB_id" field. So if you use your assignment, you end up with
$tableA["tableB_id"] //id of the related tableB, in your case null
$tableA["tableB"] //your newly assigned model, which has nothing to do with your relationship, because that should work on tableB_id
Just don't use weird array syntax for relations

how to get all related model IDs in Yii?

Suppose $model hase some items (one to many relationship), So in Yii $model->items returns an array of item models. How can I get an array of IDs of related items. This means each element of returned array is an integer.
You should simply write your own function for this, e.g.
public function getItemsIDs()
{
$ids = array();
foreach($this->items as $item)
$ids[] = $item->id;
return $ids;
}
After you just have to call $model->itemsIDs.
EDIT : as darkheir said in its comment, you should consider using DAO.
Here is an example of direct query, run from Model:
$this->getDbConnection()->createCommand("SELECT id FROM items WHERE model_id = :modelId")->bindParam(":modelId", $model->id, PDO::PARAM_STR)->queryColumn();
In result you will get numeric Array() with IDs from the table as values.
Another variant.
Yii::app()->db->createCommand("SELECT id FROM items WHERE model_id=".$model->id)->queryColumn()
This will get all IDs from table as array

Categories