Laravel / Eloquent - Column name as variable - php

Eloquent offers a handy way of passing stuff to the database
$table_name = new TableName;
$table_name->column_name = "some data";
$table_name->save();
I've got quite a lot of data from a form that needs to be validated, so I was wondering if it was possible to replace the column name with a variable, so I can put it in some loop, and get the names and data from arrays.
$table_name->$columns[$i] = $data[$i];
(though I suppose not written in that way)
Update
In the end I've gone with the following:
$table_name = new TableName;
$nameArray=[
1 => 'form-name-1',
...
];
$columnArray=[
1 => 'column_name_1',
...
];
for($i=1;$i<=count($nameArray);$i++){
if(logic logic logic) $table_name->{$columnArray[$i]} = $_POST[$nameArray[$i]];
else $table_name->{$columnArray[$i]} = NULL;
}
$table_name->save();

You can do one these:
1) Create with values
$table_name = new TableName([
"column_name" => "column_value"
]);
2) Fill
$table_name = new TableName();
$table_name->fill([
"column_name" => "column_value"
]);
3) The way you suggested originally:
$valuesMap = [
"column_name" => "column_value"
];
$table_name = new TableName();
foreach ($valuesMap as $column => $value) {
$table_name->{$column} = $value; //The {} are optional in this case
}
Note that to do 1 or 2 all fields you put in the array must be fillable and not guarded.

You can use it like an associative array:
$table_name['column_name']
$column_name = 'column_name';
$table_name[$column_name];
Or you can loop their attributes
foreach($table_name->getAttributes() as $value => $key){
echo "The attr ".$key." has this value: ".$value;
}

Related

Laravel Bulk Update for multiple record ids

I want to mass update my records in Laravel but the records not getting updated. I have a different record for each Id. Below is what I am trying.
$ids = [5,6,8,9],
$updated_array = [
['name' => 'tarun'],
['name' => 'Akash'],
['name' => 'Soniya'],
['name' => 'Shalu'],
];
Model::whereIn('id', $ids)->update($updated_array);
Mass updates are used when you're trying to update multiple rows to the same values. You cannot mass update with different values.
Therefore, this would work, but would update all matching records to name of 'tarun':
Model::whereIn('id', $ids)->update(['name' => 'tarun']);
For your example, you could do:
foreach($ids as $key => $id) {
Model::where('id', $id)->update($updated_array[$key]);
}
But as far as I know, there's no way to do this without running 4 queries in Laravel and writing a raw SQL statement to accomplish this would even be messy.
You can use Laravel Upsert for mass update.
For example :
User::query()->upsert([
['id' => 1, 'email' => 'dadan#example.com'],
['id' => 2, 'email' => 'satria#example.com'],
], 'email');
This feature available in Laravel 8 or newer
Some good solutions for this issue are on the following post: https://github.com/laravel/ideas/issues/575
1) Can create custom function to do this, which uses raw SQL, as per barryvdh comment in the post.
public static function updateValues(array $values)
{
$table = MyModel::getModel()->getTable();
$cases = [];
$ids = [];
$params = [];
foreach ($values as $id => $value) {
$id = (int) $id;
$cases[] = "WHEN {$id} then ?";
$params[] = $value;
$ids[] = $id;
}
$ids = implode(',', $ids);
$cases = implode(' ', $cases);
$params[] = Carbon::now();
return \DB::update("UPDATE `{$table}` SET `value` = CASE `id` {$cases} END, `updated_at` = ? WHERE `id` in ({$ids})", $params);
}
This apparently increase performance by 13x
2) As per another comment, another idea is to do it on a per record basis, but do the whole thing as a single transaction, which makes it much faster.
DB::beginTransaction();
// your loop and updates;
if( !$user )
{
rollbackTransaction();
} else {
// Else commit the queries
commitTransaction();
}
3) There is a laravel library also that appears to try and solve this issue. https://github.com/mavinoo/laravelBatch
Note: I have not tried or tested any of the above solutions.
I think the only way to do this without n queries would be to
(optional) backup table
grab the records and create an array with all the updated data
bulk delete the records
bulk insert from array
That's 3 queries.
Iterating through n records is not practical for my application either; I was hoping there was an alternative but it looks like I'm going to have to implement this.
We can Insert and update batch (bulk) in laravel using this Package
Laravel Batch
Please try the below code:
$data_to_be_updated = [ ['id'=>5,'name' => 'tarun'], ['id'=>6, 'name' => 'Akash'],
['id'=>8, 'name' => 'Soniya'], ['id'=>9,'name' => 'Shalu']
];
foreach ($data_to_be_updated as $key => $value) {
$data = Model::where('id',$value['id'])->first();
if ($data) {
$data->name = $value['name'];
$data->save();
}
}
you can do it like this above mentioned:-
foreach($ids as $key => $id) {
Model::where('id', $id)->update($updated_array[$key]);
}

Batch insert and arithmetic subtraction Yii 2

I have this function that doesn't batch insert to my database, I only used the batch insert function recently because back then I only used object inserts through for loops like this
$subject = ActiveCurriculum::find()
->select('scstock.*')
->joinWith('schead')
->where(['schead.TrNo' => $TrNo])
->one();
$activesubject = new ActiveSubject();
$activesubject->clientid = $clientid;
$activesubject->TrNo = $subject->TrNo;
$activesubject->LINE = $subject->LINE;
$activesubject->period = $subject->schead->period;
$activesubject->subjectcode = $subject->subjectcode;
$activesubject->schedday = $subject->schedday;
$activesubject->schedtime = $subject->schedtime;
//remember to use schead if the value is joined from another table.
$activesubject->section = $subject->schead->section;
$activesubject->roomcode = $subject->roomcode;
$activesubject->units = $subject->units;
$activesubject->save();
//reduces the slot of ccsubject by 1
$subject->slots = $subject->slots - 1;
//never forget the saving part
$subject->save();
I am not able to use this this time because I needed to insert an array of values so as I said I opted to this.
$subjects = ActiveCurriculum::find()
->select(['scstock.*', 'schead.*'])
->leftJoin('schead', 'schead.TrNo = scstock.TrNo')
->where(['sectiongroup' => $group])
->asArray()
->all();
// $activesubject = new ActiveSubject();
$bulkInsertArray = [];
foreach ($subjects as $values) {
$bulkInserArray[] = [
'clientid' => $clientid,
'TrNo' => $values['TrNo'],
'LINE' => $values['LINE'],
'period' => $values['period'],
'subjectcode' => $values['subjectcode'],
'schedday' => $values['schedday'],
'schedtime' => $values['schedtime'],
'section' => $values['section'],
'roomcode' => $values['roomcode'],
'units' => $values['units'],
];
if (count($bulkInsertArray) > 0) {
$columnNameArray = ['clientid', 'TrNo', 'LINE', 'period', 'subjectcode', 'schedday', 'schedtime', 'section', 'roomcode', 'units'];
// below line insert all your record and return number of rows inserted
$insertCount = Yii::$app->db->createCommand()
->batchInsert('subjectcontainer', $columnNameArray, $bulkInsertArray)
->execute();
}
}
But I am not able to
$subject->slots = $subject->slots - 1;
$subject->save();
like in the first one because of the arrays, can you tell me how to do this in my second code because I need to subtract the slots column by 1 every iteration of the for loop this time. Thank you.
You can perform subtraction using single query. Something like this:
$columnToUpdate = ['slots' => new \yii\db\Expression('[[slots]] - 1')];
$condition = ['sectiongroup' => $group];
ActiveCurriculum::updateAll( $columnToUpdate, $condition );
It will execute SQL:
UPDATE `active_curriculum` SET `slots`=`slots` - 1 WHERE `sectiongroup`=1234
You just need to make correct condition that correctly selects necessary rows. Alternatively, you can collect primary keys of records (IDs) when you prepare $bulkInserArray and use them for condition:
$condition = ['id' => $subjectIDsToUpdate];
This condition will build id IN (...) automatically.

Save _POST values to an array

I have an array of the names of my POST variables to use when I update a row in my database.
$jobs = array( "proposal_id",
"will_provide",
"general_scope",
"per_bid",
"job_type");
Using this style my table is called jobs and each value in the array is a column id.
I want to edit this array so each item (column id) contains a single _POST Value
Then I have a function that uses the variables to create generic queries.
function save_data($jobs) {
foreach ($jobs as $job)
{
$job[$job[$i]] = _$Post[$job];
or
Table_name[column] = cell value;
...
...
...
I would like to be able to save $values into the post variables associated to it. Something like
For example if I was going to manually create this array it would look like
$jobs = array('proposal_id' => '12345678','title_of_project' => 'aTitle','creator' => 'aUser','last_modified' => '0000-00-00','date_created' => '0000-00-00','price' =>'1000');
This should be what you're looking for:
$jobs = array( "proposal_id",
"will_provide",
"general_scope",
"per_bid",
"job_type");
$jobValues = array();
foreach($jobs as $job) {
$jobValues[] = isset($_POST[$job]) ? $_POST[$job] : null;
}
$jobs = array_combine($jobs, $jobValues);

How to streamline filtering in Mongo database

I am attempting to filter a report by using two parameters (name and id) in a Mongo DB. The way I have it set up now is this:
// if both params are empty
if ((empty($filterParams['name'])) && (empty($filterParams['id']))) {
$views = $mongoDb->selectCollection('views')->find([], []);
}
// if one of the two params are empty
if ((!empty($filterParams['name'])) || (!empty($filterParams['id']))) {
// name is empty
if ((empty($filterParams['name']))) {
$idQuery = array('id' => (int)$filterParams['id']);
$views = $mongoDb->selectCollection('views')->find($idQuery, []);
}
// id is empty
if ((empty($filteredParams['id']))) {
$nameQuery = array('name' => $filterParams['name']);
$views = $mongoDb->selectCollection('views')->find($nameQuery, []);
}
// neither are empty
if ((!empty($filterParams['name'])) && (!empty($filterParams['id']))) {
$fullQuery = array('id' => (int)$filterParams['id'], 'name' => $filteredParams['name']);
$views = $mongoDb->selectCollection('views')->find($fullQuery, []);
}
}
I was wondering if there was a way to streamline this so that the insertion can be done one time without the multiple if statements.
You are of course overengineering a very simple problem. All you really need to do here is take your "filterParams" ( which is likely from a request source ) and convert a possible "string" value for "id" into the the "numeric" format your database seems to be expecting ( going by the code written here ).
// $filterParams = array() // or it might be blank
$filterParams = array( "id" => "1", "something" => "else" );
$query = array();
foreach( $filterParams as $key => $value ) {
if ( $key == "id" )
$query[$key] = intval($value);
$query[$key] = $filterParams[$key];
}
$views = $mongoDb->selectCollection('views')->find($query);
So just start with an empty object and simply add in or tranform the keys are required for the query. If there are no keys at all, then this is an "empty" query object as MongoDB expects, with one key or both, then the same thing would also be passed through. All you really "need" here is to cast a "string" into the expected type for a given field.
In fact, especially since "filterParams" seems to be void of other data you could just alter the values in place rather than define another structure.
// $filterParams = array() // or it might be blank
$filterParams = array( "id" => "1", "something" => "else" );
$query = array();
foreach( $filterParams as $key => $value ) {
if ( $key == "id" )
$filterParams[$key] = intval($value);
}
$views = $mongoDb->selectCollection('views')->find($filterParams);
With most dynamic languages, MongoDB queries are really just data structures in the same format as what the language natively uses to express such things. So the basic principles apply to data structure manipulation anywhere.
Same JavaScript thing for example:
//var filterParams = {};
var filterParams = {
id: "1",
something: "here"
};
Object.keys(filterParams).forEach(function(key) {
if (key == "id" )
filterParams[key] = parseInt(filterParams[key]);
});
db.collection.find(filterParams);
Also "id" is typically an identifier used for a "unique" value to an object, just as _id is always used by MongoDB. So consider that when this key is present, then all other possible query arguments become redundant, unless you "really" want to check that the correct identifier was presented with the other correct properties of the object.

Associative array's loaded from database and display as 'columns'

I am trying to create an overview of product properties, for an invoice system.
So far, most things are comming together using classes and PDO.
I have the following issue.
In my class, i've created a function that builds my products array.
It loads some information from the database, to build this array.
This array, i want to use to display all the products i have selected:
$prod1 - $prod1Name - $prod1Descr - $prod1Price
$prod2 - $prod2name - $prod2Descr - $prod2Price
etc.
I figured that the Associative array would help me creating columns.
Though the problem is, that i do not understand a bit how to create multiple lines and columns this way.
I was thinking of something like:
$prod[1]["name"] - $prod[1]["descr"] - etc
Then to use this in a foreach loop to create as many new lines as required.
The only thing i could come up with is on my index.php (as shown below), cause using an index (the [1] defenition) does not seem to work the way i think it should be implemented.
For my understanding, i assigend the var in my class as an array, then redefine an array when loading the database information.
Could anyone tell me how i could try to solve this issue?
I have the following class:
<?
class Invoice{
var $vendorID;
var $product = array();
function product_array(){
global $db;
$query = $db->conn->prepare('
SELECT ProductName, ProductDescription, ProductDuration, ProductPriceInclVat, ProductPriceExclVat, ProductVatType
FROM products WHERE VendorID = :VendorID
');
$array = array (
'VendorID' => $this->vendorID
);
$query->execute($array);
$result = $query->fetchall();
if (empty($result)){
echo"Could not find any products matching your criteria.";
die;
} else {
foreach($result as $row) {
$this->product = array("Name" => $row['ProductName'],
"Description" => $row['ProductDescription'],
"Duration" => $row['ProductDuration'],
"PriceExclVat" => $row['ProductPriceExclVat'],
"PriceInclVat" => $row['ProductPriceInclVat'],
"VatType" => $row['ProductVatType']
);
}
}
}
}
?>
and then i have the following code on my index.php:
<?
$invoice = new Invoice();
foreach ($invoice->product as $key => $value){
echo $key . "<br>";
echo $value . "$value";
echo "<br>";
}
?>
When you are assigning the result arrays to the product property you are overwriting the array every time. You need to append to the array instead, so something like:
$this->product = array();
foreach($result as $row) {
$this->product[] = array(...);
}
Alternatively, you could just assign the results of fetchAll to the product property if you don't need to rename the field keys (or you could alias them in the SQL).
$query = $db->conn->prepare('
SELECT ProductName as Name,
ProductDescription as Description,
ProductDuration as Duration,
ProductPriceInclVat as PriceInclVat,
ProductPriceExclVat as PriceExclVat,
ProductVatType as VatType
FROM products WHERE VendorID = :VendorID
');
$array = array (
'VendorID' => $this->vendorID
);
$query->execute($array);
$product = $query->fetchall(PDO::FETCH_ASSOC);
The $product is now in the format you require.
After this you can avoid foreach loop in class invoice.
Other thing i noticed that you have made function product_array() which is not called,
so in index.php you are getting empty array (defined in class Invoice).
So in Invoice class it should be
$product = product_array()
and product_array function should return the value.

Categories