CsvBulkLoader import/update only existing objects - csv

I'm using a simple CsvBulkLoader to bulk update dataobjects.
class OrderImporter extends CsvBulkLoader {
public $delimiter = ';';
public $enclosure = '"';
public $hasHeaderRow = true;
public $columnMap = array(
'ID' => 'ID',
'Bezahlt' => 'Payed',
'Geandert' => 'NeedReview'
);
}
My problem is, that I don't want to create new objects, If they are in the import file. I only want to update the existing ones.
Is there a way to achieve this? Sadly I can't find anything in the docs.

I think you'd have a look at CsvBulkLoader::processRecord(). This is where each line is processed. You could try in your OrderImporter class (untested):
protected function processRecord($record, $columnMap, &$results, $preview = false) {
// find existing object
$existingObj = $this->findExistingObject($record, $columnMap);
return ($existingObject)
? parent::processRecord($record, $columnMap, $results, $preview)
: false;
}
HTH, wmk

You need to set $duplicateChecks based on what fields already existing in your DB can be checked against the import file. If the IDs match you can use:
public $duplicateChecks = array(
'ID' => 'ID',
);
You should test the import on a dev server first, especially if you use a combination of fields, as the results can be different from what you're expecting.
See $duplicateChecks in the BulkLoader api http://api.silverstripe.org/3.1/class-BulkLoader.html

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

Retrieve specific data using JSON decode Laravel

I'm new to Laravel. I need to retrieve specific data from the database using the JSON decode. I am currently using $casts to my model to handle the JSON encode and decode.
This is my insert query with json encode:
$request->validate([
'subject' => 'required|max:255',
'concern' => 'required'
]);
$issue = new Issue;
$issue->subject = $request->subject;
$issue->url = $request->url;
$issue->details = $request->concern;
$issue->created_by = $request->userid;
$issue->user_data = $request->user_data; //field that use json encode
$issue->status = 2; // 1 means draft
$issue->email = $request->email;
$issue->data = '';
$issue->save();
The user_data contains {"id":37,"first_name":"Brian","middle_name":"","last_name":"Belen","email":"arcega52#gmail.com","username":"BLB-Student1","avatar":"avatars\/20170623133042-49.png"}
This is my output:
{{$issue->user_data}}
What I need to retrieve is only the first_name, middle_name, and last_name. How am I supposed to achieve that? Thank you in ADVANCE!!!!!
As per the above code shown by you it will only insert data into the database.For retrieving data you can make use of Query Builder as i have written below and also you can check the docs
$users = DB::table('name of table')->select('first_name', 'middle_name', 'last_name')->get();
I will recommend using Resources. It really very helpful laravel feature. Check it out. It is a reusable class. You call anywhere and anytime.
php artisan make:resource UserResource
Go to your the newly created class App/Http/Resources/UserResource.php and drfine the column you want to have in your response.
public function toArray($request) {
return [
"first_name" => $this->first_name,
"middle_name" => $this->middle_name,
"last_name" => $this->last_name
]
}
Now is your controller you can use the UserResource like folow:
public function index()
{
return UserResource::collection(User::all());
}
Or after inserting data you can return the newly added data(f_name, l_name...)
$user = new User;
$user->first_name= $request->first_name;
$user->middle_name= $request->middle_name;
$user->last_name= $request->last_name;
$user->save();
$user_data= new UserResource($user);
return $user_data;

Laravel update json column in postgresql

In postgresql db I have a column type json call it "activities". I would like to add there new (key -> value) pair. Without delete existing data.
At this moment my code look like this:
$user = User::with('userLife')->where(['id' =>$id])->first();
$user->userLife->confirmation_token = null;
$user->userLife->activities = ['emailConfirmed' => Carbon::now()->timestamp];
$user->email_confirmed = true;
$user->push();
In my Model I added:
protected $casts = [
'activities' => 'array',
];
But when I use this code all other data in column is removed and only 'emailConfirmed' appeared there.
So how to do it properly to just add new value in column without removing previous?
Thank you.
You should add it as new element through another variable:
$activities = $user->userLife->activities;
$activities['emailConfirmed'] = Carbon::now()->timestamp;
$user->userLife->activities = $activities;

Yii2 codeception dataFile fixture can't be found

I am trying to run simple test and insert two record in db via fixture dataFile.
What I get is :
[ReflectionException] Class C:\xampp\htdocs\codeception\frontend\tests/_data\user.php does not exist
The file is obviously there. My UserTest.php looks like this:
<?php
class UserTest extends \Codeception\Test\Unit
{
public function _fixtures()
{
return [
'class' => \frontend\tests\fixtures\UserFixture::className(),
'dataFile' => codecept_data_dir() . 'user.php'
];
}
public function testValidation()
{
$user = new \common\models\User();
$user->setUsername(null);
$this->assertFalse($user->validate(['username']));
$user->setUsername('aaaaaaaaaaaaaaaaaaaaaaaaaa');
$this->assertFalse($user->validate(['username']));
$user->setUsername('toma');
$this->assertTrue($user->validate(['username']));
}
public function testIfUserExist()
{
$this->tester->seeRecord('user', ['name' => 'Toma']);
}
}
I saw the linux like forward slash in the error but don't know how to change it. Not sure if this is the problem because I had the same path with some images and it was fine then. What can cause this ? Thank you!
You're using seeRecord() in a wrong way. First argument needs to by class name, including namespace, so you should use it like this:
$this->tester->seeRecord(\frontend\models\User::class, ['name' => 'Toma']);

Yii model: Dynamic table relations

Table.linkedIndex is related to LinkedIndex.ID. The value of the field LinkedIndex.TableName is either Linked1 or Linked2 and defines which of these tables is related to a row in Table.
Now i want to make a dynamical link with Yii models so that i can easily get from a Table row to the corresponding Linked1 or Linked2 row:
Table.linkedID = [LinkedIndex.TableName].ID
Example
Table values:
LinkedIndex values:
Now I should get the row from Linked2 where ID=2:
$model = Table::model()->findByPk(0);
$row = $model->linked;
Model
In the model Table, I tried to make the relation to the table with the name of the value of linkedIndex.TableName:
public function relations()
{
return array(
'linkedIndex' => array(self::HAS_ONE, 'LinkedIndex', array('ID' => 'linkedIndex')),
'linked' => array(
self::HAS_ONE,
'linkedIndex.TableName',
array('ID' => 'linkedID'),
)
)
}
But then I get the error:
include(linkedIndex.TableName.php) [function.include]: failed to open stream: No such file or directory
Is there any way to make a dynamic relation Table.linkedID -> [LinkedIndex.TableName].ID with Yii Models?
Per the Yii docs here:
http://www.yiiframework.com/doc/api/1.1/CActiveRecord#relations-detail
I'd suggest using self::HAS_ONE instead (unless there can be multiple rows in LinkedIndex with the same ID - although from the looks of above, I doubt that's the case).
You can link tables together that have different keys by following the schema:
foreign_key => primary_key
In case you need to specify custom PK->FK association you can define it as array('fk'=>'pk'). For composite keys it will be array('fk_c1'=>'pk_с1','fk_c2'=>'pk_c2').
so in your case:
public function relations(){
return array(
'linkedIndex' => array(self::HAS_ONE, 'LinkedIndex', array('ID' => 'linkedIndex')),
);
}
where LinkedIndex is the class name for the LinkedIndex model (relative to your Table model - i.e. same folder. You could change that, of course) and array('ID' => 'linkedIndex') specifies the relationship as LinkedIndex.ID = Table.linkedIndex.
Edit
Looking at your updated example, I think you're misunderstanding how the relations function works. You're getting the error
include(linkedIndex.TableName.php) [function.include]: failed to open stream: No such file or directory
because you're trying to create another relation here:
'linked' => array(
self::BELONGS_TO,
'linkedIndex.TableName',
array('ID' => 'linkedID'),
)
This part: linkedIndex.TableName refers to a new model class linkedIndex.TableName, so Yii attempts to load that class' file linkedIndex.TableName.php and throws an error since it doesn't exist.
I think what you're looking for is to be able to access the value TableName within the table LinkedIndex, correct? If so, that's accessible from within the Table model via:
$this->linkedIndex->TableName
This is made possible by the relation we set up above. $this refers to the Table model, linkedIndex refers to the LinkedIndex relation we made above, and TableName is an attribute of that LinkedIndex model.
Edit 2
Per your comments, it looks like you're trying to make a more complex relationship. I'll be honest that this isn't really the way you should be using linking tables (ideally you should have a linking table between two tables, not a linking table that says which 3rd table to link to) but I'll try and answer your question as best as possible within Yii.
Ideally, this relationship should be made from within the LinkedIndex model, since that's where the relationship lies.
Since you're using the table name as the linking factor, you'll need to create a way to dynamically pass in the table you want to use after the record is found.
You can use the LinkedIndex model's afterFind function to create the secondary link after the model is created within Yii, and instantiate the new linked model there.
Something like this for your LinkedIndex model:
class LinkedIndex extends CActiveRecord{
public $linked;
public static function model($className = __CLASS__){
return parent::model($className);
}
public function tableName(){
return 'LinkedIndex';
}
public function afterFind(){
$this->linked = new Linked($this->TableName);
parent::afterFind();
}
//...etc.
}
The afterFind instantiates a new Linked model, and passes in the table name to use. That allows us to do something like this from within the Linked model:
class Linked extends CActiveRecord{
private $table_name;
public function __construct($table_name){
$this->table_name = $table_name;
}
public static function model($className = __CLASS__){
return parent::model($className);
}
public function tableName(){
return $this->table_name;
}
//...etc.
}
which is how we dynamically create a class with interchangeable table names. Of course, this fails of the classes need to have separate operations done per-method, but you could check what the table_name is and act accordingly (that's pretty janky, but would work).
All of this would result in being to access a property of the linked table via (from within the Table model):
$this->linkedIndex->linked->foo;
Because the value of LinkedIndex.TableName and Table.linkedID is needed to get the values, I moved the afterFind, suggested by M Sost, directly into the Table-Class and changed its content accordingly. No more need for a virtual model.
class Table extends CActiveRecord {
public $linked; // Needs to be public, to be accessible
// ...etc.
public function afterFind() {
$model = new $this->linkedIndex->TableName;
$this->linked = $model::model()->findByPk( $this->linkedID );
parent::afterFind();
}
// ...
}
Now I get the row from Linked2 where ID=2:
$model = Table::model()->findByPk(0);
$row = $model->linked;