I have a SectorModel with this function:
public function update(Sector $sector) {
$this->db->where('sector_id', $sector->getScetor_id());
return $this->db->update(_SECTOR_, $sector);
}
There are times that I’ll change only the name of the Sector object:
$Sector = new Sector();
$Sector->setSector_name = 'test';
$this->SectorModel->update($Sector);
The generated select looks like:
UPDATE realestate_sector SET sector_name = 'teste', sector_description = NULL
It will update but will set all other properties to NULL because it was not set on my object.
Right now, I have to fill the whole object before sending it.
Is there a way to map the Sector class and update only what was sent on the object?
Thanks in advance for any help.
Sorry for any typos, my English is not good =)
Just loop through all your object's properites and then if any is NULL just drop it with unset.
Here is your model's method edited to achieve that:
public function update(Sector $sector)
{
foreach($sector as $k=>$v)
{
if($v === NULL)
unset($sector->$k)
}
$this->db->where('sector_id', $sector->getScetor_id());
return $this->db->update(_SECTOR_, $sector);
}
Here you can find some info about iterating objects in PHP
The easiest to do this would be to rather use a array - docs here http://codeigniter.com/user_guide/database/active_record.html#update - you just create a array of all the columns with their values that you want to update and perform a $this->db->update('mytable', array('name' => 'test'), array('id' => $id)); call. This will only update the columns you specified in the First array. With the second array acting as your WHERE expression.
The only reason I can think of as to why your other values are being set to NULL is because in your example you create a new instance of the class and the other values must either have been set to nothing or are set to NULL. It would (If this is the case) be better to get a record from the table and then change and values on the populated record and pass that to the function to update.
Hope that helps.
Related
My controller looks like this:
public function show($id)
{
$model = MyModel::with([
'model2.model3.model4:id,value',
...
]);
if (myCondition) {
unset($model->model2->model3->model4);
$model->model2->model3->model4 = Model4::where('value', 'Some Value')->first();
}
return $audit;
}
In certain condition I'd like to override the result from the query with another value from the Model4 to return the good data to the client.
But I want to know if there is another way with laravel to do that. Actually I have to use unset and then push the new content if I want to change the value of the model4 property. If I don't use unset the object isn't changed, the value new value assigned to model4 is ignored I don't know why I can't just write this line
$model->model2->model3->model4 = Model4::where('value', 'Some Value')->first();
So I want to know why I can't see changes in my json object when I don't use unset and I want to know if there is anotehr way to deal with laravel for my situation ?
You can simply use setRelation method.
if ($myCondition) {
$model->model2->model3->setRelation('model4', Model4::where(...)->first());
}
(Updated) I use the laravel-translatable package and trying to insert rows with translations. When trying to save, it gives me the error "call to a member function save() on string".
I loop an object with keys and values, like: "food": "Nourriture",
and inside the loop I do a select of the Translations table:
$translationKey = \App\Translation::select('group', 'key')->where('group',
'global')->where('key', $key)->first();
I don't do exactly as the documentaion, which would have been:
$translationKey = \App\Translation::where('key', $key)->first();
The difference is that I select the columns 'group' and 'key', and I do an extra "where" to specify that group = global. Isthere anything wrong there?
Then I try to check if there is an already existing translation. If not, I insert the translation:
if($translationKey->hasTranslation('fr')) {
continue;
}else{
//insert
$translationRow = $translationKey->translateOrNew('fr')->$key = $value;
$translationRow->save();
}
I use translateOrNew instead of translate , because otherwise I get error: "Creating default object from empty value".
It seems I can't do the ->save() method because it's a string, not a model instance which it should be. So I guess there is something wrong with this line?:
$translationKey = \App\Translation::select('group', 'key')->where('group',
'global')->where('key', $key)->first();
But what is the problem?
I had some mistakes - I needed to select the whole row instead of individual columns:
$translationKey = \App\Translation::where('group', 'global')
->where('key', 'about_us')
->first();
And there were mistakes when saving the translation. My translations_translations table has a "value" column, so this worked:
$translationKey->translateOrNew($locale)->value = $value;
$translationKey->save()
Looks like that is not the new question. But I have not found out any real solutioin.
Here is my code to expected it will work:
$update_pet=pets::where("pet_id",$pet['pet_id'])->get();
if($update_pet->count()>0){
$update_pet=$update_pet->first();
$update_pet->pet_breed_id=$pet_new['pet_breed_id'];
$update_pet->save();
}
I am sure that $pet['pet_id'] and $pet_new['pet_breed_id'] has value.
And I am sure table pet_breeds in database has primary key as pet_id. The system can connect the database as I can get the pet_id and new pet_breed_id.
And I am sure I have overwrite table name and primaryKey value in model.
class pets extends Model
{
protected $table="pets";
protected $primaryKey="pet_id";
}
And it does not updated even.
Per now I am just directly using DB::update() to run the update query to solve the problem.
But I still want to know why it is happening? Or is it something wrong in the coding? Or the save function cannot used in update situation now?
Why make things complicated?
pets::find($pet['pet_id'])->update(['pet_breed_id' => $pet_new['pet_breed_id']]);
Also either include this line:
protected $guarded = [];
or this one:
protected $fillable = ['pet_breed_id'];
in your pets model class.
One last thing, you should start all your classes name with capital. And model names should not be a plural. So...
class Pet extends Model
Try to get an object instead of collection:
$pet = pets::find($pet['pet_id']);
if (!is_null($pet)) {
$update_pet->pet_breed_id = $pet_new['pet_breed_id'];
$update_pet->save();
}
Also, make sure you're getting the right object by putting dd($pet); right after the first line of the code.
You only need to change get() to first() so it will only return one data.
$update_pet=pets::where("pet_id",$pet['pet_id'])->first();
if($update_pet->count()>0){
$update_pet=$update_pet->first();
$update_pet->pet_breed_id=$pet_new['pet_breed_id'];
$update_pet->save();
}e
Or if you need to update all the record that match with where condition, use foreach
$update_pet=pets::where("pet_id",$pet['pet_id'])->get();
foreach ($update_pet as $pet) {
if($pet->count()>0){
$pet=$update_pet->first();
$pet->pet_breed_id=$pet_new['pet_breed_id'];
$pet->save();
}
}
You using get method which gives you result as an array so don't do that using first method. If pet_id is your primary key.
$update_pet=pets::where("pet_id",$pet['pet_id'])->first();
if($update_pet->count()>0){
$update_pet=$update_pet->first();
$update_pet->pet_breed_id=$pet_new['pet_breed_id'];
$update_pet->save();
}
and what is kind of stuff you are doing $update_pet->first() in line no 3 .
Long time reader, first time asker. I'm experienced with things like Java/C but PHP is new to me.
I'm having an issue where an assignment doesn't assign to where I'd expect it to.
I'm getting an array from a MySQL database via Eloquent methods, in particular:
$result= TableA::where('tableA.id', '=', $id)
->with('tableB.tableC')
->get();
For reference, printing $result out looks like this:
[{"id":105, /*TableA fields*/, "tableB":null},
{"id":106, /*TableA fields*/, "tableB":null},
{"id":107, /*TableA fields*/, "tableB":{/*tableB fields*/, "tableC":
{"id":104, /*TableC fields*/}}},
{"id":108, /*TableA fields*/, "tableB":{/*tableB fields*/, "tableC":
{"id":105, /*TableC fields*/}}}]
In some cases a TableA tuple will have an associated record in tableB and hence tableC, other times there isn't an associated record in tableB. If there isn't an associated record I want to go through and make a temporary "dummy" record to pass through instead of passing through null. The code I'm using to do so is:
for ($i=0; $i < count($result); $i++)
{
if($result[$i]["tableB"] == null)
{
Log:info($result); //Print line A
$result[$i]["tableB"] = OtherController::makeDummyTableB(); //Assignment line
Log::info($result); //Print line B
Log::info($result[$i]["tableB"]); //Print line C
}
}
The problem is that the assignment line doesn't assign to the "tableB" field in the object/array returned in $result. Printing $result out at print line A and B gives the same result, with "tableB" being null for the first two records. Print line C however gives the output I'm expecting, which is the dummy record I'm creating
{"tableC":{/*TableC fields*/}}
So the assignment is doing something, but it's not assigning to the field in $result that's already there, and instead is assigning it to somewhere else (That doesn't show up when I attempt to display it)
If anyone could let me know what my current code is actually doing, and how to have it do what I'm expecting (replace "tableB":null with "tableB":{"tableC":{/*TableC fields*/}} ) it'd be much appreciated
Eloquent models have a lot of "magic" going on in the background. The fields from the table are loaded into an attributes property, and the relationships are loaded into a relations property.
The issue you're running into is that tableB is a relationship field, not a table field. Its data is stored in the relations property. The relations property, however, is not directly modifiable the way you are attempting. When you attempt to modify it using $result[$i]["tableB"], that code is actually modifying the tableB field in the attributes property.
Then there is the issue of getting the data. When you attempt to read the data directly using $result[$i]["tableB"], it will first look in the attributes property, and if it isn't found there, then it will look in the relations property.
However, when you dump the entire object using Log::info($result);, any data in the relations property overwrites the data in the attributes property. So, after a direct assignment using $result[$i]["tableB"] = 'asdf', Log::info($result) will not show the change (since relations overwrites attributes), whereas Log::info($result[$i]["tableB"]) will show the change (since it looks at attributes first).
So, analyzing your code, we have:
for ($i=0; $i < count($result); $i++) {
if($result[$i]["tableB"] == null) {
// At this point:
// - tableB relation is null
// - tableB attribute does not exist
// This is a full dump, so the relations overwrites the attributes.
// tableB will show null
Log:info($result);
// After this assignment executes:
// - tableB relation will be null
// - tableB attribute will be the dummy object
$result[$i]["tableB"] = OtherController::makeDummyTableB();
// This is a full dump, so relations (null) overwrites the attributes (dummy object).
// tableB will show null
Log::info($result); //Print line B
// This is direct access, which accesses attributes (dummy object) before relations (null).
// tableB will show dummy object
Log::info($result[$i]["tableB"]); //Print line C
}
}
If you really want to go about it this way, you should use the setRelation() method:
$result[$i]->setRelation('tableB', OtherController::makeDummyTableB());
That will specifically set tableB on the relations property, which is what you're trying to do. That should get everything working for you.
Having said that, you may be able to tackle this a different way. If you're using Laravel >= 5.3 and your tableB relationship is a HasOne (5.3+) or a BelongsTo (5.4+) relationship, you can use the withDefault() functionality on the relationship so that it will automatically generate a default model when one does not exist in the database. You can read more about this in the documentation here.
So, your relationship definition would look something like:
public function tableB()
{
return $this->hasOne('App\TableB')->withDefault();
}
Now, when no tableB record exists, the relationship will load with a new empty TableB object, instead of null.
If you need something more than just an empty TableB object, you can pass a function to the withDefault() method, and that function will be used to generate the default object.
Eloquent does not return a plain PHP array, when using the get method it will return an instance of Illuminate\Support\Collection and you cannot simply assign an object like that into a Collection. To do so, you have to convert the collection to an array first:
$result = TableA::where('tableA.id', '=', $id)
->with('tableB.tableC')
->get();
// Convert collection to array
$result = $result->toArray();
for ($i=0; $i < count($result); $i++)
{
if($result[$i]["tableB"] == null)
{
Log:info($result); //Print line A
$result[$i]["tableB"] = OtherController::makeDummyTableB(); //Assignment line
Log::info($result); //Print line B
Log::info($result[$i]["tableB"]); //Print line C
}
}
Now you should have your value correctly assigned.
Try
foreach ($result as $whatever)
{
if($whatever->tableB == null)
{
$whatever->tableB = OtherController::makeDummyTableB();
}
}
The problem is that you can't assign a tableB object to a array field of your tableA model...
Your tableB model most likely (I assume, depends how you built it) is related to your tableA model via a "tableB_id" field. So if you use your assignment, you end up with
$tableA["tableB_id"] //id of the related tableB, in your case null
$tableA["tableB"] //your newly assigned model, which has nothing to do with your relationship, because that should work on tableB_id
Just don't use weird array syntax for relations
I am using the Active Record design pattern in my web app, and after looking at the code in my class that handles CRUD (create, read, update and delete) on a table in the DB, i'm wondering if there's a better way to set the data.
Let's say my table has around 15 columns, and my class has 15 member variables to represent the columns. I have a form which allows a user to input data for each of those variables, and on submit, my script reads everything from _POST and _GET and compiles it into an array called $params.
Currently my class setter methods looks like this:
public function setData($data1, $data2, $data3,...,$dataN-1){
$this->data1 = $data1;
$this->data2 = $data2;
$this->data3 = $data3;
...
$this->dataN-1 = $dataN-1;
}
I was wondering, what the problems would be if I changed the setData argument to be an array, specifically the array containing the _POST and _GET values, so something like this
public function setData($data){
$this->data1 = $data['data1'];
$this->data2 = $data['data2'];
$this->data3 = $data['data3'];
...
$this->dataN-1 = $data['dataN-1'];
}
Assume that all the form element names are correct. Obviously, the time will be saved when I call the method, not having to list 15 arguments. I am going to be using prepared statements for all INSERTs and UPDATEs. Are there any other pitfalls that I should be aware of doing this?
Thanks a lot for your help.
Why don't you use an array too for $this->data[] in your class?
You could this way use a foreach and could maintain/automatize it more easily, fetching data or setting it in your DB.
EDIT : this way you fill your array
in your class :
private $data = array(
'db_column_name_1' => null,
'db_column_name_2' => null,
'db_column_name_3' => null,
...
'db_column_name_n' => null
);
public function setData($data){
foreach ( $this->data as $key => $value )
$this->data[$key] = $data[$key] ;
}
Symmetrically you could as easily make the right INSERT/UPDATE query