In one of my models I need to update another attribute when two other attributes from the same model are updated.
In my case, using an Accessor is not an option, because in some cases I will need to do a database query looking for full_phone_number.
What is the best to achive the desired result?
This is my model:
class Address extends Model {
protected $fillable = [
'country_code',
'phone_number',
'full_phone_number',
];
}
When I create a new Address, I need the full_phone_number column to be automatically populated:
$address = Address::create([
'country_code' => 55,
'phone_number' => 1199999999
]);
The expected result is:
# Address model on DB
{
"country_code": 55,
"phone_number": 1199999999,
"full_phone_number": 551199999999
}
When I update country_code or phone_number from an Address, I need the full_phone_number column to be automatically updated:
$address->update([
'phone_number' => 1188888888
]);
The expected result is:
# Address model on DB
{
"country_code": 55,
"phone_number": 1188888888,
"full_phone_number": 551188888888
}
I got the desired result using events, but I don't know if this is the best way to handle this case. The same could be converted to an observer.
class Address extends Model {
protected $fillable = [
'country_code',
'phone_number',
'full_phone_number',
];
protected static function boot()
{
parent::boot();
static::updating(function ($address) {
$address->full_phone_number = $address->country_code . $address->number;
});
}
}
Related
I'm quite new to Laravel and I was not able to find the answer to this problem neither on Laravel docs, nor here.
I guess it's just a matter of how to search for it, cause I'm pretty sure it's a common case.
I have two models in relationship (this is a simplified case), I retrieve the info I need through a Resource file, but I'm not able to understand how to properly store or update info.
Here's a code example:
Models\Company.php
class Company extends Model
{
protected $fillable = [
'name', 'blablabla', 'country_id', 'blablabla2',
];
public function country() {
return $this->belongsTo(Country::class);
}
}
Models\Country.php
class Country extends Model
{
protected $fillable = [
'code', 'name', 'prefix', 'tax_code_id',
];
public function companies() {
return $this->hasMany(Company::class);
}
}
Then I have a CompanyController file to manage API requests:
Controllers\CompanyController.php
class CompanyController extends BaseController
{
public function index()
{
$companies = Company::paginate();
$response = CompanyResource::collection($companies)->response()->getData(true);
return $this->sendResponse($response, 'Companies retrieved successfully');
}
public function store(Request $request)
{
$input = $request->all();
$validator = Validator::make($input, $this->validation_rules);
if($validator->fails()){
return $this->sendError('Validation error.', $validator->errors());
}
$company = Company::create($input);
return $this->sendResponse($company->toArray(), 'Company added successfully.');
}
}
...
public function update(Request $request, Company $company)
{
$input = $request->all();
$validator = Validator::make($input, $this->validation_rules);
if($validator->fails()){
return $this->sendError('Validation error.', $validator->errors());
}
$company->update($input);
return $this->sendResponse($company->toArray(), 'Company updated successfully.');
}
And here the CompanyResource I'm using to display info as I need.
Resources/CompanyResource.php
class CompanyResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'blablabla' => $this->blablabla,
'country' => $this->country,
'blablabla2' => $this->blablabla2,
];
}
}
So when retrieving Companies (or single company) I get a nested JSON:
{
"id": "1",
"name": "something",
"blablabla": "blablabla",
"country": {
"id": "100",
"code": "MA",
"name": "Mars",
"prefix": "123",
"tax_code_id": "#$%"
},
"blablabla2": "blablabla2"
}
If I create or update a new company I send a payload that has the same structure of what I'm getting above, but if I edit country id value my company model doesn't get it.
PUT Api/companies/1
{
"name": "something",
"blablabla": "blablabla3",
"country": {
"id": "200",
"code": "JU",
"name": "Jupiter",
"prefix": "456",
"tax_code_id": "#=%"
},
"blablabla2": "blablabla2"
}
I'm expecting to update country_id field in companies table for record 1 so that it matches payload (so going from 100 to 200), but it's not happening.
I could edit frontend logic in order to send only country_id in payload since I'm not going to update countries table and all that additional info is redundant, but I'd like to know how to manage it in controller with Laravel.
Would you mind helping me? Thanks in advance.
If you want it to work with the code now, you need to have country_id in the root JSON object you are sending. As this is the way you would fill the id. This is not the best approach in my opinion, but this is why your update is not working at the moment.
{
"name": "something",
"blablabla": "blablabla3",
"country_id": 200,
...
I actually like the approach of sending complete objects. Commonly to fill id's is not good, as it can interfere with the way relations work. Laravel will set your relationships when you associate, if not you are not guaranteed to have the correct relationship after the fill.
Therefor i would fetch out the id and associate the country object with the company. In a logic similar to this.
// only fill non relation fields, fill used as save is needed after associate()
$company->fill($request->only(['name', 'blabla']));
$company->country()->associate(Country::find($request->get('country')['id']));
//associate does not save
$company->save();
I wrote a gist for this years ago that can relate any two models regardless of their relationship type. You just need to supply it with the name of the relationship method: https://gist.github.com/kmuenkel/055f107139d904e30810bf53750d9c6e
Model - Promo:
...
protected $table = 'promo';
...
public function locations()
{
return $this->belongsToMany(Cities::class, 'cities_promo');
}
Controller in laravel-admin
...
protected function form()
{
$location = Cities::pluck('name', 'id');
$form = new Form(new Promo);
$form->text('title', __('Title'));
$form->textarea('desc', __('Description'));
$form->multipleSelect('locations')->options($location);
return $form;
}
...
The bottom line is that it does not display the values that were previously selected and saved. An empty field is displayed there, where you can select values from the City model.
An intermediate solution was to use the attribute.
It is necessary that the format for multipleSelect (and others) was in array format [1,2,3 ... ,7].
In normal communication, an array of the form is transmitted:
{
['id' => 1,
'name' => 'Moscow',
...
],
['id' => 2,
'name' => 'Ekb',
...
],
}
Therefore, for formalization, I used a third-party attribute "Cities" to the model "Promo".
...
//Add extra attribute
//These attributes will be written to the database, if you do not want
//this, then do not advertise!
//protected $attributes = ['cities'];
//Make it available in the json response
protected $appends = ['cities'];
public function getCitiesAttribute()
{
return $this->locations->pluck('id');
}
public function setCitiesAttribute($value)
{
$this->locations()->sync($value);
}
If there are other suggestions, I am ready to listen.
Thank.
change $location to
$location = Cities::All()->pluck('name', 'id');
you can return $location to know it has value or not
also you can set options manually
$form->multipleSelect('locations')->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);
to know it works
I'm staring with Laravel and I'm having troubles trying to make a simple insert, but It seems that all of my fillable fields are not being included. This is the error:
SQLSTATE[HY000]: General error: 1364 Field 'name' doesn't have a default value
(SQL: insert into `addresses` (`updated_at`, `created_at`)
values (2017-12-25 09:31:49, 2017-12-25 09:31:49))
As you can see, only created_at and updated_at are about to be inserted, I thought that maybe I forgot my fillable vars, but this is my Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Addresses extends Model
{
protected $fillable = [
'name',
'city',
'suburb',
'street',
'o_number',
'i_number',
'postal_code',
'phone_s',
'email_s',
'google_map',
'customer_id'
];
}
And the Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Addresses;
use App\Customers;
class AddressesController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function store(Request $request){
$create = Addresses::create([
'name' => request('name'),
'city' => request('city'),
'suburb' => request('suburb'),
'street' => request('street'),
'o_number' => request('o_number'),
'i_number' => request('i_number'),
'postal_code' => request('postal_code'),
'phone_s' => request('phone_s'),
'email_s' => request('email_s'),
'google_map' => request('google_map'),
'customer_id' => Customers::where('code',$request->session()->get('customer_code'))->first()->id
]);
$success = $create ? $request->session()->flash('success', '¡Registro exitoso!') : $request->session()->flash('success', 'Ooops! Algo salio mal :(');
return redirect('addresses/'.$request->session()->get('customer_code'));
}
}
Echo the request() values works! So I'm missing right now, I have some other Models and Controller working good in the same way. Please Help!
This error show because you the NAME field is required on your database. Try to edit your migration script and put default value on this field or make it nullable.
eg.
$table->string('name')->nullable();
OR
$table->string('name')->default('');
Then run a migration refresh.
Goto "phpmyadmin" >> "Variables" then find "sql_mode" edit and remove "STRICT_ALL_TABLES or STRICT_TRANS_TABLES"
It is working for me.
Hope it will help for All.
This is late answer for this question, but it might help for others.
This error can be occurred due to error in $fillable data in modal.
You can try using
protected $guarded = []
instead of
protected $fillable = [
'name',
'city',
'suburb',
'street',
'o_number',
'i_number',
'postal_code',
'phone_s',
'email_s',
'google_map',
'customer_id'
];
But You have to validate the data that you passed within the controller.
The error occurs due to the strict mode of MYSQL5.7. Please change your config/database.php in the connections.mysql section by putting 'strict' => false.
I solved it using save()
$addresses = new Addresses;
$customer_id = Customers::where('code',$request->session()->get('customer_code'))->first()->id;
$addresses->name = $request->name;
$addresses->city = $request->city;
$addresses->suburb = $request->suburb;
$addresses->street = $request->street;
$addresses->o_number = $request->onumber;
$addresses->i_number = $request->inumber;
$addresses->postal_code = $request->postal_code;
$addresses->phone_s = $request->phone_s;
$addresses->email_s = $request->email_s;
$addresses->google_map = $request->map;
$addresses->customer_id = $customer_id;
$success = $addresses->save() ? $request->session()->flash('success', '¡Registro exitoso!') : $request->session()->flash('success', 'Ooops! Algo salio mal :(');
return redirect('addresses/'.$request->session()->get('customer_code'));
It's working properly
When you use the nullable() method on a field, that field will default to NULL.
For example, add this to your migration file:
$table->string('name')->nullable();
Update:
You can add:
$table->string('name')->nullable()->default(null);
You can check Null option in table structure, like this -
Make sure request() has key 'name'.Replace request('name') to random string and try again.
In my case I forgot to change - this is my code on my Controller
public function store(Request $request)
{
$message = new Message();
$message->name = $request->input('name');
$message->name = $request->input('message');
$message->save();
}
I made a duplication of the 'name' that's why it happened and made this
public function store(Request $request)
{
$message = new Message();
$message->name = $request->input('name');
$message->message = $request->input('message');
$message->save();
}
This way the solution to the problem.
I was having this problem because I didn't add a correct column under $fillable list.
class Chirp extends Model
{
use HasFactory;
protected $fillable = [
'message', // This line should be added.
];
}
I would like to hide my foreign_key on JSON response :
return Response::json(['type' => 'success', 'data' => $my_object, 'status' => 200], 200);
I added in my model :
protected $hidden = ['fk_category_id'];
My foreign key is hide !
But in my controller I have this :
$new_question = $this->question_repository->create([
'text' => $question->text,
'fk_category_id' => 2,
]);
The problem : I don't create a new object in my database, the field fk_category_id is NULL, I think I have no access to this field
My foreign key in my JSON response it's hide (great!) but I can't set my foreign key in my database when I created new entry.
add this to your model
protected $fillable = [..,'fk_category_id','text'];
you are attempting to do mass assignment, fillable is a whitelist which field will be fillable by mass assignment. .. means you can add whatever field you want there.
I saw your entity in your last (deleted) question. Add a setter function, then you can call that! Your protected and private properties will remain hidden!
class MyEntity
{
private $hidden;
public function getHidden()
{
return $this->hidden;
}
public function setHidden($value)
{
$this->hidden = value;
}
// the rest of your code etc
}
iam trying to save model from form, that have relationship defined via junction table, but since the property is relationship object it is read-only and it fails on validation.
Model relationship:
public $payer
/**
* #return \yii\db\ActiveQuery
*/
public function getPayerRelationship()
{
return $this->hasMany(PartyRelationship::className(), ['contract_id' => 'id'])->where(['relationship' => 'P']);
}
public function getPayers(){
return $this->hasMany(ContractingParty::className(), ['id' => 'contracting_party_id'])
->via('payerRelationship');
}
public function getContractors() { // could be a static func as well
$model = ContractingParty::find()->asArray()->all();
return ArrayHelper::map($model, 'id', 'subject_name');
}
Form view:
<?= $form->field($model, 'payers')->widget(Select2::classname(), [
'data' => $model->getContractors(),
'language' => 'en',
'options' => ['placeholder' => '-- Select company --'],
'pluginOptions' => [
'allowClear' => true,
'multiple' => true,
],
'showToggleAll' => false
]) ?>
It wont validate or save, because of read-only property payers. I tried to use different property in $form->field($model, 'payer'... (instead of payers), then validation works and even saving works, but trouble is, that editing have no preselected values of that model, because they are in model->payers. And i have no idea, what iam supposed to pass here instead of this relationship object (or property of model in general).
Maybe iam plainly blind, but in manual there is a lot of information about getting data from db, but almost no info about saving.
(btw. i saw this post: Yii2 Invalid Call: Setting read-only property - but that didnt give me any new piece of information at all).
Is my form design wrong, or model design (Meaning i should just create form field using two models)? Thanks
Adding setters to model:
public function setPayer(){
$payer_id_array = array();
$payer_array = ArrayHelper::toArray($this->payers);
foreach ($payer_array as $value){
$payer_id_array [] = $value['id'];
}
$this->payer = $payer_id_array;
}
public function setRecipient(){
$recipient_id_array = array();
$recipient_array = ArrayHelper::toArray($this->recipients);
foreach ($recipient_array as $value){
$recipient_id_array [] = $value['id'];
}
$this->recipient = $recipient_id_array;
}
and manually into controller (action create and update):
$model->setPayer();
$model->setRecipient();
seems to fix the conflict between the names of relation and property passed into the field.