Part 1: Main Question
On my colours_tab table (PostgreSQL 9.6), I have a column called colours_json_col of type JSONB.
I have the following code in my Laravel controller which creates a new record on the afore-mentioned table:
$colour_rec = new ColoursModel();
$colour_rec -> colours_json_col = ['fruits' => ['apple' => 'pink'] ];
$colour_rec -> update (['colours_json_col->fruits->apple' => 'green']);
$colour_rec -> saveOrFail();
The above code doesn't error or throw an exception, yet after executing it the new record in my database contains the following JSON data:
{"fruits": {"apple": "pink"}}
Obviously, what I want is for the apple to be green, not pink! The documentation here (https://laravel.com/docs/5.5/queries#updates) suggests that I'm doing the right thing in order set it 'green'. Can anyone see what I'm doing wrong?
Part 2: Bonus Question
In order to prevent the above code from erroring, I have had to declare the column colours_json_col as $fillable in the model:
protected $fillable = ['colours_json_col'];
I don't really want to set the column as $fillable if at all possible. Therefore, is there a way to update the value without having to call the update() function?
Answering both parts of the question in one:
$colour_rec = new ColoursModel();
$colour_rec -> colours_json_col = ['fruits' => ['apple' => 'pink'] ];
$colour_rec -> setAttribute ('colours_json_col->fruits->apple', 'green');
$colour_rec -> saveOrFail();
It seems to be an undocumented feature in Eloquent, as far as I can tell.
Related
I am new to laravel.
I have an issue when I am trying to update or create record in DB.
I have a table called DspAccountFee with this columns:
I want to create record of dsp_account_id + screen_type when the combination not exists, and to update if the combination exists.
this is my code: (just tried to update the first row keys of -> dsp_account_id(5187) + screen type (ctv). However nothing changed.
DspAccountFee::updateOrCreate(
['dsp_account_id' => $dsp_account_id, 'screen_type' => 'ctv'],
['pmp_percent' =>$fields['fee_ctv_pmp_percent'], 'omp_percent' => $fields['fee_ctv_omp_percent']]
);
When I print the values before the DB operation they exists:
\Log::info("dsp_account:");
\Log::info($dsp_account_id);
\Log::info("ctv pmp percent:");
\Log::info($fields['fee_ctv_pmp_percent']);
\Log::info("ctv omp percent:");
\Log::info($fields['fee_ctv_omp_percent']);
\Log::info("app pmp percent:");
What I am missing why it is not update the db? Nothing in logs and No exception
this is my method in the model
protected $fillable = array(
'dsp_account_id', 'screen_type'
);
Check the corresponding model and make sure that those columns exist in the
$fillable property. It should look somewhat like this.
protected $fillable = [
'dsp_account_id',
'screen_type',
'pmp_percent',
'omp_percent'
];
Your updateOrCreate syntax looks okay.
To update the updated_at column in your database, you can use the touch() method: you'll need to edit your code to something like this
$foo = DspAccountFee::updateOrCreate([
'dsp_account_id' => $dsp_account_id,
'screen_type' => 'ctv'
],
[
'pmp_percent' => $fields['fee_ctv_pmp_percent'],
'omp_percent' => $fields['fee_ctv_omp_percent']
]);
$foo->touch();
I want to insert data to one table (called Startups) which has 2 "BelongsTo" relations, I found how to do this with one table (One to Many) in a good Laravel's documentation but I'm beginner in this area and I don't know how to insert data to related to 2 different tables (Categories - Contacts) in one common table (Sturtups), to understand this better please see the image I attached below
Here is my code (please don't pay attention to Sessions, it doesn't matter in that case):
$category_id = Session::get('category_id');
$country_id = Session::get('country_id');
$new_contact = new Contact([
'name' => Session::get('contact_name'),
'phone' => Session::get('contact_phone'),
'email' => Session::get('contact_email')
]);
$country = Country::find($country_id);
$country->contacts()->save($new_contact);
$new_startup = new Startup([
'name' => Session::get('startup_name'),
'description' => Session::get('startup_description'),
'url' => Session::get('startup_url'),
'logo' => Session::get('logo_name')
]);
$category = Category::find($category_id);
$category->startups()->save($new_startup);
$contact = Contact::find( $country->contacts()->id );
$contact->startups()->save($new_startup);
Database Relations image:
image of relations between tables in the DB
Additional info: I have this error:
"General error: 1364 Field 'contact_id' doesn't have a default value"
I know why that error happens (I'm trying to create Startup without id of contact)
I just want to know how insert data in that case
Thank you guys for any help!
Answer to your question is:
make that field nullable in database migration so this error will not occur
$table->unsignedInteger('contact_id')->nullable();
I am using Laravel 5.5 and I am declaring my model object the following:
$product = new product();
$product->name = $coinArr[$key];
$product->symbol = $symbolArr[$key];
$product->current_price = $priceArr[$key];
///save image to public folder
$fileName = basename($imgArr[$key]);
Image::make($imgArr[$key])->save(public_path('images/' . $fileName));
$product->asset_logo = $fileName;
//$product->updateOrCreate();
App/Product::updateOrCreate($product);
If the product does not exist in the database I would like to create it else just update it.
I tried the following two ways to use the updateOrCreate method. However, I receive the following error for App/Product::updateOrCreate($product);:
Type error: Too few arguments to function Illuminate\Database\Eloquent\Builder::updateOrCreate(), 0 passed in C:\Users\admin\Desktop\Coding Projects\laravel_proj\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php on line 1455 and at least 1 expected
And the following error for $product->updateOrCreate();:
Type error: Too few arguments to function Illuminate\Database\Eloquent\Builder::updateOrCreate()
Any suggestions how to use updateOrCreate with my model object?
I appreciate your replies!
When you use updateOrCreate, you need to choose which attributes are used to determine if the product exists already. The function takes 2 arrays:
product::updateOrCreate([
'name' => $coinArr[$key] //Laravel will check if this model exists by name
],[
'symbol' => $symbolArr[$key] //if exists, will update symbol. if doesnt exist, will create new with this name and symbol
]);
That's not how the updateOrCreate() method works. In the first parameter you put an array with search conditions. If you want to search existing route by name for example, the correct syntax will be:
Product::updateOrCreate(
[
'name' => $coinArr[$key]
],
[
'symbol' => $symbolArr[$key],
'current_price' => $symbolArr[$key],
'asset_logo' => $fileName
]
);
The second parameter is array for creating a new object.
https://laravel.com/docs/5.5/eloquent#other-creation-methods
I have created a form in which i embed another form. My question is about this embedded form - I'm using a sfWidgetFormDoctrineChoice widget with option multiple set to true. The code for this embedded form's configure method:
public function configure()
{
unset($this['prerequisite_id']);
$this->setWidget('prerequisite_id', new sfWidgetFormDoctrineChoice(array(
'model' => 'Stage',
'query' => Doctrine_Query::create()->select('s.id, s.name')->from('Stage s')->where('s.workflow_id = ?', $this->getOption('workflow_id') ),
'multiple' => true
)));
$this->setValidator('prerequisite_id', new sfValidatorDoctrineChoice(array(
'model' => 'Stage',
'multiple' => true,
'query' => Doctrine_Query::create()->select('s.id, s.name')->from('Stage s')->where('s.workflow_id = ?', $this->getOption('workflow_id') ),
'column' => 'id'
)));
}
I unset the prerequisite_id field because it is included in the base form, but I want it to be a multiple select.
Now, when I added the validator, everything seems to work (it passes the validation), but it seems like it has problems saving the records if there is more than one selection sent.
I get this PHP warning after submitting the form:
Warning: strlen() expects parameter 1 to be string, array given in
D:\Development\www\flow_dms\lib\vendor\symfony\lib\plugins\sfDoctrinePlugin\lib\database\sfDoctrineConnectionProfiler.class.php
on line 198
and more - I know, why - in symfony's debug mode I can see the following in the stack trace:
at Doctrine_Connection->exec('INSERT INTO stage_has_prerequisites
(prerequisite_id, stage_id) VALUES (?, ?)', array(array('12', '79'),
'103'))
So, what Symfony does is send to Doctrine an array of choices - and as I see in the debug sql query, Doctrine cannot render the query correctly.
Any ideas how to fix that? I would need to have two queries generated for two choices:
INSERT INTO stage_has_prerequisites (prerequisite_id, stage_id) VALUES (12, 103);
INSERT INTO stage_has_prerequisites (prerequisite_id, stage_id) VALUES (79, 103);
stage_id is always the same (I mean, it's set outside this form by the form in which it is embedded).
I have spend 4 hours on the problem already, so maybe someone is able to provide some help.
Well, I seem to have found a solution (albeit not the best one, I guess). Hopefully it'll be helpful to somebody.
Finally, after much thinking, I have concluded that if the problem comes from the Doctrine_Record not being able to save the record if it encounters an array instead of a single value, then the easiest solution would be to overwrite the save() method of the Doctrine_Record. And that's what I did:
class StageHasPrerequisites extends BaseStageHasPrerequisites
{
public function save(Doctrine_Connection $conn = null)
{
if( is_array( $this->getPrerequisiteId() ) )
{
foreach( $this->getPrerequisiteId() as $prerequisite_id )
{
$obj = new StageHasPrerequisites();
$obj->setPrerequisiteId( $prerequisite_id );
$obj->setStageId( $this->getStageId() );
$obj->save();
}
}
else
{
parent::save($conn);
}
}
(...)
}
So now if it encounters an array instead of a single value, it just creates a temporary object and saves it for each of this array's values.
Not an elegant solution, definitely, but it works (keep in mind that it is written for the specific structure of the data and it's just the effect of my methodology, namely See What's Wrong In The Debug Mode And Then Try To Correct It Any Way Possible).
I'm having an annoying problem. I'm trying to find out what fields of a form were changed, and then insert that into a table. I managed to var_dump in doUpdateObjectas shown in the following
public function doUpdateObject($values)
{
parent::doUpdateObject($values);
var_dump($this->getObject()->getModified(false));
var_dump($this->getObject()->getModified(true));
}
And it seems like $this->getObject()->getModified seems to work in giving me both before and after values by setting it to either true or false.
The problem that I'm facing right now is that, some how, sfWidgetFormSelect seems to be saving one of my fields as a string. before saving, that exact same field was an int. (I got this idea by var_dump both before and after).
Here is what the results on both var dumps showed:
array(1) {["annoying_field"]=> int(3)} array(1) {["annoying_field"]=>string(1)"3"}
This seems to cause doctrine to think that this is a modification and thus gives a false positive.
In my base form, I have
under $this->getWidgets()
'annoying_field' => new sfWidgetFormInputText(),
under $this->setValidators
'annoying_field' => new sfValidatorInteger(array('required' => false)),
and lastly in my configured Form.class.php I have reconfigured the file as such:
$this->widgetSchema['annoying_field'] = new sfWidgetFormSelect(array('choices' => $statuses));
statuses is an array containing values like {""a", "b", "c", "d"}
and I just want the index of the status to be stored in the database.
And also how can I insert the changes into another database table? let's say my Log table?
Any ideas and advice as to why this is happen is appreciated, I've been trying to figure it out and browsing google for various keywords with no avail.
Thanks!
Edit:
ok so I created another field, integer in my schema just for testing.
I created an entry, saved it, and edited it.
this time the same thing happened!
first if you what the status_id to be saved in the database, you should define your status array like this:
{1 => "a", 2 => "b", 3 => "c", 4 => "d"}
So that way he know that 1 should be rendered like "a" and so on. Then, when saving, only the index should be saved.
About saving in another database, my advise is to modify the doSave method defined by the Form class yo match your needs. I only know how Propel deals with it, maybe this could help:
the doSave method dose something like this:
protected function doSave($con = null)
{
if (null === $con)
{
$con = $this->getConnection();
}
$old = $this->getObject()->getModifiedValues($this);//Define this
$new_object = new Log($old);//Create a new log entry
$new_object->save($con));//save it!
$this->updateObject();
$this->getObject()->save($con);
// embedded forms
$this->saveEmbeddedForms($con);
}
Hope this helps!
Edit:
This is an example extracted from a model in one of my applications and its working ok:
Schema:
[...]
funding_source_id:
type: integer
required: true
[...]
Form:
$this->setWidget('funding_source_id', new sfWidgetFormChoice(array(
'choices' => array(1 => 'asdads', 2 => '123123123' , 3 => 'asd23qsdf'),
)));
$this->setValidator('funding_source_id', new sfValidatorChoice(array(
'choices' => array(1 => 'asdads', 2 => '123123123' , 3 => 'asd23qsdf'),
'required' => true
)));
About the log thing, that could be quite more complex, you should read the current implementation of the doSave method in the base form class, currently sfFomrObject on Symfony1.4., and when and how it delegates object dealing with modified values.
Okay,
It turns out I forgot to do a custom validator to use the array key instead.