Yii2 insert partial data into table - mysql

I'm inserting into a table data coming from another table which is basically a subset of the first table.
$partialProduct = partialProducts::find()
->where(['company_id' => $company_id])
->andWhere(['productName' => $productName])->one();
$partialProductData = ($partialProductData['attributes']);
$product = Yii::$app->db->createCommand()
->insert('products', [
'field1' => $field1value,
...
...
'fieldN' => $fieldN,
])->execute();
Something like that could work, but considering that partialProductData has a lot of fields, I was searching for a cleaner way of doing it.
I've tried with a foreach(partialProductData as $key => $value) approach, considering that the keys are named as the products table column names, but I struggled in obtain something viable.

As suggested, creating an array of key/values pairs should work if the keys match the table fields in both models. Something like:
$data = [];
foreach($partialProduct->attributes as $key => $value):
$data[$key] = $value;
endforeach;
$product = Yii::$app->db->createCommand()
->insert('products', $data)
->execute();
Alternatively, you could use massive assignment. Again, for this to work the fields on both models would need to match:
$product = new Product;
$product->attributes = $partialProduct->attributes;
$product->save();
If you only want to save certain fields from your partialProducts to your Product, you can use scenarios to select the specific fields you want to be to set with massive assignment:
In Product.php
const SCENARIO_TEST = 'test';
...
public function scenarios()
{
return [
self::SCENARIO_TEST => ['field_1', 'field_2'], // Any field not listed here will not be set
];
}
And wherever you're doing the save:
$product = new Product;
$product->scenario = Product::SCENARIO_TEST;
$product->attributes = $partialProduct->attributes;
$product->save();

Related

Keep sort order of json columns in Laravel after inserting new Key Value pair

I have a key value pair that I am inserting into a model with the following:
public function addContactDetail(Request $request){
$data = $request->all();
$contact_id = $data['contact_id'];
$contact = Contact::find($contact_id);
$details = $contact->details;
$details[$data['label']] = $data['value'];
$contact->details = $details;
$contact->save();
return response()->json($contact);
}
After insert it sometimes puts it randomly in the middle of the object. How do I keep it at the end?
If you are using Laravel 5 or greater version,
Try casting your json column into array in eloquent using mutators. like this.
inside your Contact Model
protected $casts = [
'details' => 'array',
];
By doing so, I guess you will get what you want. Try it and let me know

How do I make the most effective and efficient logic to check the data in the database exist or not?

I use laravel 5.6
I have a json file containing 500 thousand records. I want to create a logic to check whether the id of each record already exists or not in the database. If it doesn't already exist, then there will be a data insert process. If it already exists, there will be a data update process
I have made logic. I just want to make sure whether my logic is effective or not
My logic code like this :
$path = storage_path('data.json');
$json = json_decode(file_get_contents($path), true);
foreach ($json['value'] as $value) {
$data = \DB::table('details')->where('id', '=', $value['Code'])->get();
if ($data->isEmpty()) {
\DB::table('details')->insert(
[
'id' => $value['Code'],
'number' => $value['Number'],
...
]
);
}
else {
\DB::table('details')
->where('id', '=', $value['Code'])
->update([
'id' => $value['Code'],
'number' => $value['Number'],
...
]);
}
}
The code is working. But the process seems really long
Do you have another solution that is better?
updateOrCreate
You may also come across situations where you want to update an existing model or create a new model if none exists. Laravel provides an updateOrCreate method to do this in one step. Like the firstOrCreate method, updateOrCreate persists the model, so there's no need to call save():
// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99]
);
in your case your code should be like this (create Details model first) :
$path = storage_path('data.json');
$json = json_decode(file_get_contents($path), true);
foreach ($json['value'] as $value) {
Details::updateOrCreate(
[ 'id' => $value['Code'] ],
[ 'number' => $value['Number'], ... ]
);
}
i think that's the best way to do it. Eloquent return's a collection so you cant just validate that your string is null.

Can indexby be used on associations directly in the query?

I have a number of situations where I need to cross-reference various records by ID, and find it's easiest to do so when the array is indexed by that ID. For example, Divisions hasMany Teams, Divisions hasMany Games, and Games belongTo HomeTeam and AwayTeam. When I want to read all of the teams and games in a division, I do something like this:
$division = $this->Divisions->get($id, [
'contain' => ['Teams', 'Games']
]);
I don't do
$division = $this->Divisions->get($id, [
'contain' => ['Teams', 'Games' => ['HomeTeam', 'AwayTeam']]
]);
because it seems that would increase memory requirements, especially when I'm further containing other models (People, etc.) in the Teams. So, instead I do
$division->teams = collection($division->teams)->indexBy('id')->toArray();
after the get to reindex that array, and then when I'm iterating through $division->games, to get the home team record I use $division->teams[$game->home_team_id]. This is all well and good (except that it sets the teams property as being dirty, a minor inconvenience).
But it seems that the queryBuilder functionality of the ORM is pretty magical, and I know that I can do
$teams = $this->Divisions->Teams->find()
->where(['division_id' => $id])
->indexBy('id')
->toArray();
to get an array of teams indexed how I want, so I'm wondering if there's some way to include indexBy on the associations. I tried
$division = $this->Divisions->get($id, [
'contain' => [
'Teams' => [
'queryBuilder' => function (Query $q) {
return $q->indexBy('id');
},
],
'Games',
]
]);
but, unsurprisingly, this didn't work. Any ideas?
Just for the record, guess you know this already, indexBy() doesn't belong to the query, but to the result set, so being able to call it requires the query to be executed first. It's not possible to use this for an association query builder, as it must return a query, not a result set.
While it would be possible to use result formatters for the associations and modify the result set accordingly, the problem is that the result set will hold all team results for all divisions, and when the team entities are being distributed on the various division entities that they belong to, the arrays will be "reindexed", respectively, the arrays will be populated without respect to the indices of the result set, so long story short, that won't work.
Global result formatter
However, a result formatter for the main query should work fine, and as you probably already figured, you can simply reset the dirty state afterwards in case it causes any problems, something like
$division = $this->Divisions
->find()
->contain([
'Teams'
])
->where([
'Divisions.id' => $id
])
->formatResults(function($results) {
/* #var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\CollectionInterface */
return $results
->map(function ($row) {
if (isset($row['teams'])) {
$row['teams'] = collection($row['teams'])->indexBy('id')->toArray();
}
if ($row instanceof EntityInterface) {
$row->dirty('teams', false);
}
return $row;
});
})
->firstOrFail();
Custom association and association specific result formatters
Another option would be to use a custom association class, which overrides ExternalAssociationTrait::_buildResultMap(), so that it respects the indices of the result set, as this is where the problem starts.
By default the associated entities are fetched from the result set and appended to a new array, which is later assigned to the respective association property on the entity the results belong to. So this is where the the keys from the possible custom indexed result set are being lost.
Here's an example, the change is really small, but I'm not sure about possible side effects!
src/Model/Association/IndexAwareHasMany.php
namespace App\Model\Association;
use Cake\ORM\Association\HasMany;
class IndexAwareHasMany extends HasMany
{
protected function _buildResultMap($fetchQuery, $options)
{
$resultMap = [];
$key = (array)$options['foreignKey'];
// grab the index here
foreach ($fetchQuery->all() as $index => $result) {
$values = [];
foreach ($key as $k) {
$values[] = $result[$k];
}
// and make use of it here
$resultMap[implode(';', $values)][$index] = $result;
}
return $resultMap;
}
}
Original: https://github.com/cakephp/...ORM/Association/ExternalAssociationTrait.php#L109
Now you must of course make use of the new association, to simplify it for this example, let's just override the table class' default hasMany() method
public function hasMany($associated, array $options = [])
{
$options += ['sourceTable' => $this];
$association = new \App\Model\Association\IndexAwareHasMany($associated, $options);
return $this->_associations->add($association->name(), $association);
}
And now, finally, you could use a result formatter for the association:
$division = $this->Divisions->get($id, [
'contain' => [
'Teams' => [
'queryBuilder' => function (Query $query) {
return $query
->formatResults(function($results) {
/* #var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\CollectionInterface */
return $results->indexBy('id');
});
}
],
'Games',
]
]);

Mass update in Laravel Eloquent or DB

Is there anyone who knows how to do this without the technique of doing it in a one query string. I mean the popular ways I see on the net is by looping in data(the updates) and generating a single update statement and then fire a query. Is it possible for an Eloquent Approach or DB without looping?
This is posible with Eloquent, it might be necessary to enable mass-assignment, but you will get an error if so.
$post_data = Input::all();
$model = Model::find($id);
$model ->fill($post_data);
$model ->save();
or
$post_data = Input::all();
Model::find($id)->update($post_data);
Yes, you can do that but in that case, you have to make the array of data that is a loop is needed to store the data in the array with respective field_name => value of the table.
The following is the example:
$Array = array(); //This is needed to hold data while looping over $YourData
$YourData - is the array of data you want to store in the respective table.
foreach ($YourData as $YourDatakey => $YourDatavalue ){
$Array = [
'table_column_name' => $YourDatavalue['value_from_array'],
'table_column_name' => $YourDatavalue['value_from_array'],
'table_column_name' => $YourDatavalue['value_from_array'],
...... and so on
];
}
$InsertQuery= YourModelName::create($Array);
PS:
YourModelName model file should have the columns in protected
$fillable = ['column1','column2'....];
You should use App\Models\ModelName; at the top of the file.

How can I insert data from a table from sql into a dropdown list in zend framework 2?

I need to insert multioptions to a dropdown list, options taken from a table from my database.
I created the elements like:
$this->add(array(
'name' => 'company',
'type' => 'Zend\Form\Element\Select',
//'multiOptions'=> $options,
'options' => array(
'label' => 'Company',
),
'attributes' => array(
'style' => "float:right;",
),
));
I want to choose from a dropdown list some values that are in a table in my database. For example I have the entity Contacts and I need to choose for the contact a company that is in a table named companies in the database.
After reading on zend framework's site, I tried using this code:
$params = array(
'driver'=>'Pdo_Mysql',
'host'=>'localhost',
'username'=>'root',
'password'=>'',
'dbname' =>'myDataBase'
);
$db = new \Zend\Db\Adapter\Adapter($params);
$sql= new Sql($db);
$select = $sql->select();
$select ->from('companies')
->columns(array('id','company_name'))
->order(" 'company_name' ASC");
I also read on some other sites that I could use a function:
$options = $sql->fetchPairs('SELECT id, name FROM country ORDER BY name ASC');
but it seems it doesn't exist anymore in Zend Framework 2.
Please guys, give me a hand. If the code isn't good and you have a better idea, please tell me.
Thanks in advance!
This is just a quick and dirty answer, but i guess it can get you started.
Create a ServiceFactory, this should be done in a separate factory class instead of a closure, but i still use a closure - faster to write ;)
Get the config from the ServiceLocator so you have access to the DB-Params
Create your default SQL Stuff to retriefe the value_options
Populate the value_options using the setValueOptions($valueOptions) function of your given form-element
Module.php getServiceConfig()
return array(
'factories' => array(
'my-form-factory' => function($serviceLocator) {
$form = new My\Form();
$config = $serviceLocator->get('config');
$db = new \Zend\Db\Auth\Adapter\Adapter($config['dbParams']); //or whatever you named the array key
$sql = //do your SQL Stuff
// This is a fake array, it should be your $sql result in the given format
$result = array('value' => 'label', 'value2' => 'label2');
$form->get('elementToPopulate')->setValueOptions($result);
return $form;
}
)
);
SomeController.php someAction()
$form = $this->getServiceLocator()->get('my-form-factory');
return new ViewModel(array(
'form' => $form
));
I hope this gets you started
you have to add that field validation on controller for setting value in it.
$select = $db->select()->where("state_code = ?",$arr["state_code"]);
$resultSet = $cityObj->fetchAll($select);
$cityArr = $resultSet->toArray();
$city_ar = array();
foreach($cityArr as $city){
$city_ar[$city['id']] = $city['company'];
}
$form->company->setMultiOptions($city_ar);
$form->company->setValue($val["company"]);
by using this code drop down of country have the value that are in resultset array ($resultSet).