Laravel iterate array and write to Model with Relations - php

I think about dynamic model creation or updating an model.
Let's assume i have an array like this:
$data = array(
'first_name' => 'Max',
'last_name' => 'Power',
'invoiceAddress.city' => 'Berlin',
'invoiceAddress.country_code' => 'DE',
'user.status_code' => 'invited'
);
Now i would like to iterate that array, and write the data to an model, where the dot notation tells me that i must write to an relation.
Normal code:
$model->first_name = $data['first_name'];
$model->last_name = $data['last_name'];
$model->invoiceAddress->city = $data['invoiceAddress.city'];
and so on.
I would prefer a more dynamic way:
foreach($data as $key => $value){
$properties = explode('.',$key);
//Now the difficult part
$model[$properties[0]][$properties[1]] = $value;
//Would work for invoiceAddress.city,
//but not for first_name
}
Here is the problem, that i don't know how many properties the explode will create.
Is there a way to solve such problem in a dynamic way?

You could use the Illuminate\Support\Arr helper from Laravel like this:
foreach($data as $key => $value) {
Arr::set($model, $key, $value);
}
It works because the Arr class uses dot notation to access the properties like:
Arr::get($model, 'invoiceAddress.country_code');
Is equivalent to:
$model['invoiceAddress']['country_code'];
If you prefer to use cleaner helper:
foreach($data as $key => $value) {
array_set($model, $key, $value);
}

You could also use these Laravel helpers data_fill() or data_set().
Other doted notation functions are listed here.

Related

Get Custom String for Key based off Values Variable Name in Class?

Trying to convert a procedural script to OOP.
In my procedural script I define $metadata = array() before I define variables in a foreach loop like such:
foreach ($productdata as $productinfo) {
$price = (float) $productinfo['Price'];
$regularprice = (float) $productinfo['RegularPrice'];
And then proceeded to manually input/type what I wanted the key value (_cost and _regular_cost)
$metadata[] =
[
'key' => '_cost',
'value' => $price
];
$metadata[] =
[
'key' => '_regular_cost',
'value' => $regularprice
];
Now I am trying to compact it into a class, but am uncertain how to generate these key => _{value} names.
Something I've thought to try.. could be totally off.
So the name of my class is WooCommerceController
Class WooCommerceController
{
protected $metadata;
static $metadata_keys = ['_cost', '_regular_cost'];
Then I thought about making the class function either accept an individual metadata value
public function generateMetaData(string $metadatavalue) {
or an array of values
public function generateMetaData(array $metadatavalue_array) {
but no matter which I can think of, even if I have access to the $metadata_keys static variable, I can't think of a way for the function to distinguish between for example $price and $regularprice.
The only thing I can think of is to pass it in a strict indexed way (ensure the same order of values being passed congruent with the values in WooCommerceController::$metadata_keys)..
or I thought maybe I could name my variables --- instead of $price, rename them $_cost --- and then I was just researching methods to converting variables name to string but this seems like it is more of a hackish solution
Can anyone think of a more proper solution?
Not sure how proper this is still very much learning OOP and OOP design patterns but I came up with this solution:
Class WooCommerceController
{
protected $metadata = array();
public function generateMetaData(string $metadatakeyname, string $metadatavalue) {
$this->metadata[] =
[
'key' => $metadatakeyname,
'value' => $metadatavalue
];
return $this->metadata;
}
And in context using it like this:
$metadata = $woo->generateMetaData('_cost', $price);
$metadata = $woo->generateMetaData('_regular_cost', $price);
And it appears to fill up the array in the class context (this->metadata) and also each additional statement adds to the $metadata array out of the class context.

Cannot foreach over an array of objects in a Laravel POST

I have an array of objects which have been posted from a Vue Axios function, which I wish to loop over and save into a database. They are answers to a question.
I have passed in $data which is the array of answer objects (each has a content, correct and mark property), and the $id of the question they belong to. When I return $data, it shows me the array of objects with all the correct properties. When I return $data[0], I can access the first object. But when I try and foreach as below, it complains that $content doesn't exist. Running count() on $data also errors. What is wrong here?
Route::post('answers/{id}', function (Request $data, $id) {
foreach ($data as $value) {
$post[] = [
'user_id' => 1,
'question_id' => $id,
'content' => $value->content,
'correct' => $value->correct,
'mark' => $value->mark
]);
}
Answer::save($post);
});
You are trying to iterate over the hole $request object, which is an instance of the Request class. To access the received values first get them:
// To get all the data
$data = $request->all();
// or..
// To get just a specific value
$data = $request->get('key');
// or..
// only a list of allowed elements
$data = $request->only('here', 'goes', 'your', 'keys');
So, in case your frontend are sending an array of items under the key items. Just get them like mentioned above:
$items = $request->get('items');
Then you can use the foreach():
$items = $request->get('items');
foreach($items as $item)
{
// your operations
}
You can read more about Retrieving Input, in the documentation.

Remove certain keys and values from associative array

I have a array that looks like this:
[['title'= >'my title','time'=>'14:00','date'=>'feb 2'],['title'= >'another','time'=>'14:00','date'=>'feb 2']]
Now I wish to remove all time and date keys from the arrays and also rename the title to text so it looks like this:
[['text'= >'my title'],['text'= >'another title']]
I have tried to use
$tags = array_map(function($tag) {
return array(
'text' => $tag['title'],
);
}, $tags);
But I cant get it to work
Laravel solution:
collect($array)->transform(function($i) { return ['text' => $i['title']]; })->toArray();
You can transform your collections,
$mycollection = $myModel->get();
return $mycollection->map(function($row){
return [
'text' => $row->title,
];
});
Or you can use Fractal: http://fractal.thephpleague.com/transformers/
$newTags = [];
foreach($tags as $tag) {
$newTags[] = [['text'] => $tag['title']];
}
$tags = $newTags;
This question isn't specific to Laravel, but since you mention it:
Use the collect() helper and it's methods for convenience. You'll want to look at pull, map, and maybe transform in particular.
If you don't use it then unset will delete the index you want from the array.
Alternatively, just create a new array:
$a = []
foreach($tags as $tag) {
$a[] = ['text' => $tag['title']];
}
edit: fix

How to get back lower case column names in Eloquent?

I am working with a database which has all uppercase snakecase column names and when I fetch them with eloquent I do something like:
foreach($data as $key => $item){
$data[$key] = array_change_key_case($item);
}
This makes the keys ie the column names to lower case, but it soon becomes inefficient since I need to nested arrays too like so:
foreach($tasks as $key => $task){
foreach($task['users'] as $innerKey => $user){
$task['users'][$innerKey] = array_change_key_case($user);
}
$tasks[$key] = array_change_key_case($task);
}
And I can't change the database. Is there a way I can make eloquent give me back the column names in lower case?
You can transform column names at the driver level using PDO attributes. To do so, set your Laravel connection options (in app/config/database.php) like so:
return array(
'connections' => array(
'mysql' => array(
'options' => array(
PDO::ATTR_CASE => PDO::CASE_LOWER,
),
),
)
)
The default is PDO::CASE_NATURAL, which is why your code sees them as the database has them stored.
Update: If you are using MySQL, you might consider setting lower_case_table_names = 2, which tells the server:
If set to 2, table names are stored as given but compared in lowercase. This option also applies to database names and table aliases.
If override the getAttribute() method on your model you can transform the key before the call:
public function getAttribute($key)
{
$databaseColumn = implode('_', array_map('ucfirst', explode('_', $key)));
return parent::getAttribute($databaseColumn);
}
This will allow you to do $model->get_model_param and it will access $model->Get_Model_Param on the model.

Laravel eloquent change attribute by field name

My model structure after model -> find($id);
protected 'attributes' =>
array (size=35)
'id' => string '1234134' (length=6)
'case' => string '123123' (length=7)
I want to update this model like this
foreach($input as $key => $value){
var_dump($key);
$case->($key)= $value;
}
$case -> save();
I tried like this but doesn't work
$reflection = new \ReflectionClass($case);
foreach($input as $key => $value){
var_dump($key);
$reflection->"$ke"y;
}
The following code should work.
$reflection = new \ReflectionClass($case);
foreach($input as $key => $value){
var_dump($key);
$reflection->{$key} = $value;
}
$case->save();
if this is a laravel eloquent model, you could utilize the use of the fill method.
$case->fill($input);
$case->save();
It seems like your syntax is a bit off, try this:
foreach($input as $key => $value){
$case->{$key} = $value;
}
$case->save();
Your problem is that you are trying to access a property of the object in a way that PHP is not supporting.
To access a property of the object by using a variable containing it's name you need to use string interpolation. Wikipedia - String Interpolation
you can achieve this by using curly brackets {} instead of round brackets ()
You can fix your example like this:
foreach($input as $key => $value){
$case->{$key}= $value;
}
$case->save();
By the way in PHP this can be achieved almost like in your second example but without the double quotes
foreach($input as $key => $value){
$case->$key= $value;
}
$case->save();
I personally prefer using curly brackets as it's showing more clearly that I'm using the string value of the variable.

Categories