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 .
Related
I've a very big doubt about how works laravel for a very simple thing:
If I call:
$companies=User::All();
Then I can use statement like this in a forach:
foreach($companies as $company)
$company['new_field']= 'something';
If i'm limiting the output of the query like:
$companies = DB::table('companies')
->select('id','name','email','business_name',...)->get();
The things doesnt work as before,
I try with or without the ->get()
I try to convert with ->toArray() (errors rised)
I try with put() and push() for collections method and agains errors...
How can I add a field in every item of the collection just to pass it to a view?
Try like this, hope it works for you:
$users=User::select('id','name','email','business_name',...)->get()->toArray();
and then use foreach loop like this:
foreach($users as $key => $value ){
$users[$key]['newField'] = "Demo";
}
If you are using Laravel and model in it so there is a better way to add custom attribute or field here is what i do for custom field
For Example :
There is a Model Name User
so in User Model
add a property name appends like :
class User extends Model
{
protected $appends = ['new_field'];
public function getNewFieldAttribute() // defining field logic here
{
return // your code
}
So you no need to use foreach and looping and adding new field
for more have a look on laravel doc : https://laravel.com/docs/5.5/eloquent-mutators#accessors-and-mutators
Suggestion
you can limit your output with Model too.
User::select('id','name','email','business_name',...)->get();
if you are making an array like
User::select('id','name','email','business_name',...)->get()->toArray();
so this will also give you your custom field
I have a situation where I need a specific attribute accessor appended to one of my models automatically:
class Mission extends Eloquent {
protected $appends = ['launch_date_time'];
public function getLaunchDateTimeAttribute() {
return ($this->attributes['launch_approximate'] == null) ? $this->attributes['launch_exact'] : $this->attributes['launch_approximate'];
}
}
As you can see, this launch_date_time property is dependent on two other fields of my model that are actually in my database.
However, I now want to perform a query where only a certain number of fields are returned, as this is going to be sent over AJAX multiple times and I would rather use as few resources as possible:
// AJAX GET
// missions/all
public function all() {
$allMissions = Mission::with('featuredImage')->get(['mission_id', 'name', 'featured_image']);
return Response::json($allMissions);
}
The issue here is that I no longer need the launch_date_time attribute, so I have excluded it, **in doing so, my AJAX request does not work successfully:
Undefined index: launch_approximate on line 78 of H:\myproj\app\models\Mission.php
This is clearly because my model is attempting to append launch_date_time, of which launch_approximate is a dependency of. If I include all the required dependencies, all of them any my attribute that I want to append appear:
$allMissions = Mission::with('featuredImage')->get(['mission_id', 'name', 'featured_image', 'launch_approximate', 'launch_exact', 'launch_date_time']);
This is undesirable. Is there a solution where I can keep both setups?
The reason it is not working is because you are not retrieving the required fields from the database in the get method on your query. That is why you can't access launch_exact and launch_approximate because they are not set in the instance of your model.
So to make it work like you want. You would have to check if launch_exact and launch_approximate are set before you access them.
public function getLaunchDateTimeAttribute() {
if(isset($this->attributes['launch_approximate']) && $this->attributes['launch_exact']) {
return ($this->attributes['launch_approximate'] == null) ? $this->attributes['launch_exact'] : $this->attributes['launch_approximate'];
}
return null;
}
You can also set a whitelist with the $visible property and a black list with $hidden inside your model to not show certain attributes when outputing to json or a array take a look at the docs: http://laravel.com/docs/5.1/eloquent-serialization#hiding-attributes-from-json
First code:
$result=ResPlaces::model()->findall($criteria);
/*foreach($result as $value)
{
$model_name=ResRegistration::model()->findByAttributes(array('id'=>$value->user_id));
$model_image=ResImages::model()->findByAttributes(array('place_id'=>$value->id),array('limit'=>'1'));
}*/
}
echo CJSON::encode($result);
?>
i need to add model_name->company_name & $model_image->image
to my echo json array
Try to load the relations within the findAll so you don't need the foreach.
ResPlaces::model()->with('ResRegistration', 'ResImages')->together()->findAll($criteria);
For ResRegistration and ResImages use the relation names as defined in your model.
If you don't need all fields of these relations, you can specify a select in your $criteria.
See the guide for more info.
edit: I am not quite sure, why you cannot use relation. However you will need a loop, like you already have. Here is how you do it without relations:
First add the fields you want to use to your model. In this case
class ResPlaces extends CActiveRecord
{
public $name; // check that these do not collide with your models db fields
public $image;
[...]
}
In your controller do the loop like you did before:
foreach($result as $key => $value)
{
$result[$key]->name = ResRegistration::model()->findByAttributes(array('id' => $value->user_id));
// findByAttributes returns only one record, so you don't need the limit here
// if you want multiple records, you have to use findAllByAttributes
$result[$key]->image = ResImages::model()->findByAttributes(array('place_id' => $value->id), array('limit' => '1'));
}
That should do it. However I wouldn't recommend this way, because you have lots of additional database requests. If your $result is populated with say 100 records, you have in sum 200 additional queries which are not nessassary with a relation.
Note also that if you need these two other fields more often, it may be better to put these two queries which are now in the controller in your model. The afterFind() would be the right place.
I have a table with a field called vat_free. So my model was created with a property $vat_free. Its value can be 0 or 1.
I want my view to show No or Yes, instead of 0 or 1. I can do it creating a getter like getVatFree(), but it seems like a messy solution, because then I'll have two properties to the same field, even though it would serve different purposes.
So how can I use only the original property $vat_free? Couldn't I modify its getter?
Creating method
public function getVatFreeString(){
return $this->vat_free ? 'Yes':'No';
}
Is proper solution, it's not messy.
You could do like
$vat_free = YES or NO
but right before save this object you would override object class with beforeSave() method like following:
beforeSave(){
if($this->vat_free = YES){
$this->vat_free = 1
}else{
$this->vat_free = 0;
}
}
and override afterFind() to do the reverse(for beforeSave()) translate. But this is even messy and will not work if u do bulk save or retrieve.
I see 2 solutions.
Go with what you have said getVatFree(), this is whole purpose of OOP encapsulation.
Instead of making 1 or 0 in db, do Y or N values, you can use them in both places without problems.
In your model, create a new field that will be used for display purposes only.
class User extends CActiveRecord
{
public $displayVatFreeFlag;
public function rules() { ... }
public function afterFind()
{
$this->displayVatFreeFlag = ($this->vat_free ? 'Yes':'No');
}
}
Then, in your field, display the field as normal.
Vat free : <?php echo $model->displayVatFreeFlag; ?>
I am raising a Yii event on beforeSave of the model, which should only be fired if a specific property of the model is changed.
The only way I can think of how to do this at the moment is by creating a new AR object and querying the DB for the old model using the current PK, but this is not very well optimized.
Here's what I have right now (note that my table doesn't have a PK, that's why I query by all attributes, besides the one I am comparing against - hence the unset function):
public function beforeSave()
{
if(!$this->isNewRecord){ // only when a record is modified
$newAttributes = $this->attributes;
unset($newAttributes['level']);
$oldModel = self::model()->findByAttributes($newAttributes);
if($oldModel->level != $this->level)
// Raising event here
}
return parent::beforeSave();
}
Is there a better approach? Maybe storing the old properties in a new local property in afterFind()?
You need to store the old attributes in a local property in the AR class so that you can compare the current attributes to those old ones at any time.
Step 1. Add a new property to the AR class:
// Stores old attributes on afterFind() so we can compare
// against them before/after save
protected $oldAttributes;
Step 2. Override Yii's afterFind() and store the original attributes immediately after they are retrieved.
public function afterFind(){
$this->oldAttributes = $this->attributes;
return parent::afterFind();
}
Step 3. Compare the old and new attributes in beforeSave/afterSave or anywhere else you like inside the AR class. In the example below we are checking if the property called 'level' is changed.
public function beforeSave()
{
if(isset($this->oldAttributes['level']) && $this->level != $this->oldAttributes['level']){
// The attribute is changed. Do something here...
}
return parent::beforeSave();
}
Just in one line
$changedArray = array_diff_assoc($this->attributes,
$this->oldAttributes);
foreach($changedArray as $key => $value){
//What ever you want
//For attribute use $key
//For value use $value
}
In your case you want to use if($key=='level') inside of foreach
Yii 1.1: mod-active-record at yiiframework.com
or Yii Active Record instance with "ifModified then ..." logic and dependencies clearing at gist.github.com
You can store old properties with hidden fields inside update form instead of loading model again.