Laravel: Update single field of table using a Resource Controller - php

I have a Resource Controller (with all the actions: index, create, store, show, edit, update and destroy) and I was wondering what is the best approach to edit a single field column?
Let's say we have a Users table with name, email, password and active (active is a tiny int 0 or 1).
In the users management page, there is a button to activate/deactivate users (makes a request to the server to update the "active" field for the selected user).
Should I create a new method updateStatus in the Controller or is there a way to handle this using the update method?
I don't want, by mistake, allow empty values in the name, email or password when updating the "active" column, so I need to keep the validation rules (in short, all fields are required), but this means when updating the "active" field, I need to pass all the user data in the request.
At this point I'm very confused and all help will be appreciated.
Thanks in advance!

When you send an instance from edit action to the form , all the data will be sent and you can edit one or more columns if you need .
For instance :
public function update(Request $request , $id) {
$data = YourModel::find($id);
$data->someColumn = $request->someColumn;
$data->save();
}
other fields that you didn't send any value for them will be saved as they were before . for this you can set the form like below :
{!! Form::model($yourInstance,['route'=>['someRoute.update','id'=>$yourInstance->id],'method'=>'PATCH',]) !!}

It sounds like you are new to Laravel, and some key concepts can be hard to grasp.
In my opinion the best way to do it would be via a Model class. This is slightly confused by the fact that Laravel has a built in Users model, so I'm going to use a different model as the example of how to update a db field.
php artisan make:model MyData
Will create a new empty model file for the MyData table in app/
The file will look like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MyData extends Model
{
//
}
Even though there's nothing in there, it now allows you do alter the database table using Eloquent.
In your controller add this to make sure the model is included:
use App\MyData as MyData;
The controller should have a method something like this if updating with user input from a form:
public function updateStatus(MyData $myData, Request $request){
$myData->where('id', $request->id)->update(['active' => $request->active]);
}
You could do the exact same thing like this:
public function updateStatus(Request $request){
$data = MyData::find($request->id);
$data->active = $request->active;
$data->save();
}
Both approaches make sense in different circumstances.
See https://laravel.com/docs/5.5/eloquent#updates

Related

CakePHP 4 - how to validate forms that need to save data to multiple tables

Apologies if this has been asked before. All of the examples I can find are old or apply to legacy versions of CakePHP, e.g. cakephp: saving to multiple models using one form is 7 years old.
I have an application in CakePHP 4.1.6. Two of the tables in the database are called tbl_users and tbl_orgs ("orgs" in this case means "Organisations").
When I add an Organisation I also want to create a User who is the main contact within the Organisation. This involves saving to both the tbl_orgs and tbl_users tables when the form is submitted.
The problem I'm experiencing is how to get the form working in a way where it will run the validation rules for both tbl_users and tbl_orgs when submitted.
This is how our application is currently structured:
There is a Controller method called add() in src/Controller/TblOrgsController.php. This was generated by bake and was initially used to insert a new Organisation into the tbl_orgs table. At this point it didn't do anything in terms of tbl_users however it worked in terms of saving a new Organisation and running the appropriate validation rules.
One validation rule is that every companyname record in tbl_orgs must be unique. If you try to insert more than 1 company with the name "My Company Limited" it would give the validation error "This company name already exists":
// src/Model/Table/TblOrgsTable.php
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add(
$rules->isUnique(['companyname']),
[
'errorField' => 'companyname',
'message' => 'This company name already exists',
]
);
return $rules;
}
Whilst the above applies to TblOrgs we also have an buildRules() in TblUsers which applies similar logic on an email field to make sure that all email addresses are unique per user.
In the add() Controller method we start by specifying a new empty entity for TblOrgs:
// src/Controller/TblOrgsController.php
public function add()
{
$org = $this->TblOrgs->newEmptyEntity();
// ...
$this->set(compact('org'));
}
When the form is created we pass $org:
// templates/TblOrgs/add.php
<?= $this->Form->create($org) ?>
<?= $this->Form->control('companyname') ?>
<?= $this->Form->end() ?>
When the TblOrgs fields are rendered by the browser we can inspect the HTML and see these are obeying the corresponding Model. This is clear because of things such as required="required" and maxlength="100" which correspond to the fact that field is not allowed to be empty and is a VARCHAR(100) field in the database:
<input type="text" name="companyname" required="required" id="companyname" maxlength="100">
It also works in terms of the rules specified in buildRules for TblOrgs. For example if I enter the same company name twice it shows the appropriate error in-line:
I then tried to introduce fields for TblUsers. I prefixed the form fields with dot notation, e.g. this is intended to correspond to tbl_users.email input field:
<?= $this->Form->control('TblUser.email') ?>
When inspecting the HTML it doesn't do the equivalent as for TblOrgs. For example things like maxlength or required are not present. It effectively isn't aware of TblUsers. I understand that $org in my Controller method is specifying a new entity for TblOrgs and not TblUsers. I reviewed the CakePHP documentation on Saving With Associations which says
The save() method is also able to create new records for associations
However, in the documentation the example it gives:
$firstComment = $articlesTable->Comments->newEmptyEntity();
// ...
$tag2 = $articlesTable->Tags->newEmptyEntity();
In this case Tags is a different Model to Comments but newEmtpyEntity() works for both. With this in mind I adapted my add() method to become:
$org = $this->TblOrgs->TblUsers->newEmptyEntity();
But this now gives an Entity for TblUsers. It seems you can have either one or the other, but not both.
The reason this doesn't work for my use-case is that I can either run my Validation Rules for TblOrgs (but not TblUsers) or vice-versa.
How do you set this up in a way where it will run the validation rules for both Models? It doesn't seem to be an unreasonable requirement that a form may need to save data to multiple tables and you'd want the validation rules for each of them to run. I get the impression from the documentation that it is possible, but it's unclear how.
For reference there is an appropriate relationship between the two tables:
// src/Model/Table/TblOrgsTable.php
public function initialize(array $config): void
{
$this->hasMany('TblUsers', [
'foreignKey' => 'o_id',
'joinType' => 'INNER',
]);
}
and
// src/Model/Table/TblUsersTable.php
public function initialize(array $config): void
{
$this->belongsTo('TblOrgs', [
'foreignKey' => 'o_id',
'joinType' => 'INNER',
]);
}
Okay, lots of confusion to clear up here. :-) My assumption here, based on what you've written, is that you're trying to use a single form to add a new organization, and the first user in it, and then maybe later you'll add more users to the org.
First, $this->TblOrgs->TblUsers is your users table object, so when you use
$org = $this->TblOrgs->TblUsers->newEmptyEntity();
what you're doing is creating a new user entity. The fact that you got to that table object through the orgs table, and that you're calling it $org doesn't change that. It doesn't somehow magically create a blank org entity with a blank user entity in it. But you don't need that entity structure here at all here, just the empty org entity. Go back to simply:
$org = $this->TblOrgs->newEmptyEntity();
Now, in your form, you'll want something like this:
<?= $this->Form->create($org) ?>
<?= $this->Form->control('companyname') ?>
<?= $this->Form->control('tbl_users.0.email') ?>
<?= $this->Form->end() ?>
The field is called tbl_users.0.email because:
The table name gets converted to lower case underscore format.
It's a hasMany relation from orgs to users, so it's expecting an array of users; we have to give a numeric index into that array, and 0 is a great place to start. If you were going to add a second user at the same time, the field for that would be tbl_users.1.email.
Note: A great way to figure out what format the form helper is expecting you to create your field names in is to read an existing set of records from the database (in this case, an org and its users), and then just dump that data, with something like debug($org);. You'll see that $org has a property called tbl_users, which is an array, and that will point straight to this structure I've described above.
With the fields set up like this, you should be able to patch the resulting data directly into your $org entity in your controller, and save that without any other work. The patch will created the entire structure, with a entity of class TblOrg, with a tbl_users property which is an array containing a single entity of class TblUser, and validation will have been done on both of them. (At least it should; you can use debug($org); as mentioned above to confirm it.) And when you save this entity, it will first save the TblOrg entity, then add that new ID into the TblUser entity before saving it, as well as checking the rules for both and making sure that nothing gets saved to the database if it can't all be saved. That all happens automatically for you with the single save call!
If your association was a hasOne or belongsTo relation (for example if you were adding a new user and also the org that they're in, instead of the other way around), you could dump a sample $user, and see that it has a property called tbl_org which is just a straight-up entity, not an array of entities, and note that tbl_org is now singular, because it's just one entity instead of a bunch. In this case, the field name to use would be tbl_org.companyname, no array index in there at all.

Laravel 8 user table column names

I am using laravel 8 with an existing user table. All is working as expected except the password reset link functionality. This is because my table has the email column name as "Email" instead of "email." Other applications use this table, so the column name cannot be changed. I can get the password reset link functionality working if I manually set the column name within the framework itself (example below).
File: /vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
public function retrieveByCredentials(array $credentials)
{
// framework code that retieves the user record for email address
if ($res) {
$res->email = $res->Email;
}
// rest of frame work code
}
This seems a little "hacky." Is there a better approach to this?
Laravel would benefit greatly from more customization regarding the user's table (custom user table name, column names, etc.).
Laravel has mutators and accessors. This does that you can change behavior of ->email access or assigning it. Add this snippet to your User.php model.
class User {
public function getEmailAttribute()
{
return $this->attributes['Email'];
}
}
You can read the docs about it. The naming convention for the function is get{PropertyName}Attribute, if you define your function like so, you can easily overwrite property logic in Laravel. Making it use the column Email.

Intermediate Table with Laravel

I'm working in a Webapp and I have a problems to work with a intermediate table, these are my tables in mysql:
User:
Integer:id
String:name
String:email
String:phone
Exercise:
Integer:id
String:name
String:description
User_Exercise:
Integer:id
Integer:id_user
Integer:id_exercise
Integer:record
So, what I want to do is that when I create an exercise, it be created one row for each user with the exercise-id that I have created it before. Later the user could change his record in this exercise.
I have thought to create a model to handle the user_exercise's table but I don't know if there is some way to do this better or not.
So, There are some way to do this without create a new model?
PD: Sorry for my terrible english
You don't need a seperate model for User_Exercise
You can use $this->belongsToMany from base Model i.e., User
Note :
For insert process you can get the parent id by
$insertUser = User::create($userData);
then
$insertUser->id for taking the last insert id
And then to retrieve with respect to User_Exercise you shall use $this->belongsToMany from your User Model
Example
Have this in your User Model
public function getUser() {
return $this->belongsToMany('App\User', 'excercise_name', 'user_id', 'excercise_id')->select(array('exercise.id', 'excercise.name'));
}
And Get the data you need from any Controller like this
$userData = User::find($userId)->getUser;

How to I set a field in a form to auto increment based on the primary key ID?

I'm new to Agiletoolkit and still learning the ropes.
Here's my question:
I have a regular CRUD for managing jobs.
$this->add('CRUD')->setModel('Job');
Model contains field "job_number" which is should automatically be filled-in on the "Add" page: N762, N763, etc.
Is this something that can be done in the model or the addjob page being called from the CRUD table?
Can any one give me some very simple code example?
Thanks
I believe this should be done on Model level. It basically have nothing to do with View level.
If this job_number field is strictly related with ID, then you can even make it as expression field in model and don't even store it in database. For example, job_number = id + 100.
But if you really want to store it in database, then you should probably do like this:
1) in model create job_number as ordinary field with type('number'), but with ->system(true) or ->editable(false) depending on where you want to see this field (form, grid);
2) in model init method add hook afterInsert.
function init(){
parent::init();
// ... your field definitions here
// add afterInsert hook
$this->addHook('afterInsert',array($this,'afterInsert'));
}
3) in model create method
function afterInsert($m,$new_id){ // <-- new_id is ID of newly inserted record
$m->load($new_id);
$m['job_number'] = $new_id + 100; // <-- your function here
$m->save();
}
or maybe you can even write it simpler - not sure
function afterInsert($m,$new_id){ // <-- new_id is ID of newly inserted record
$this->set('job_number',$new_id + 100); // <-- your function here
$this->save();
}
This should work, but I didn't test this. Just writing here on the fly.
Try this out and if you need more help just let us know. Also you're more than welcome to join ATK4 IRC channel and ask your questions directly.
Something like this should address your question:
in model init:
$this->addField("job_no")
->defaultValue("N" . $this->dsql()->del("fields")->field(
$this->dsql()->expr("max(id) + 1"), "nr"
)->getOne());
Not tested, though.

Data can't save after alter the table in yii

I new in yii framework. i create an application in yii framework. i created model, controller, views using gii. After that i alter database table. I deleted 2 column and add 3 new columns. After that overwrite the model using the gii. But when i am trying to save into that table it show property(which was old column that I deleted) is not defined. Plz provide me a solution for this.
You need to define all columns in the validation rules() method in your model, have a look and make sure that you have defined a rule for every column in the table there, for example (if it's a string with max length 128):
public function rules()
{
return array(
...
array('myField', 'length', 'max'=>128),
...
);
}
See some info about validation rules.
Also, for forms if you're using CActiveForm widget and calling fields like so:
echo $form->labelEx($model,'myField');
echo $form->textField($model,'myField');
Then you'll need to make sure that a label is defined in the model too, in the attributeLabels() method, for example:
public function attributeLabels()
{
return array(
...
'myField'=>'My Field',
...
);
}
Lastly, if you want the field to be searchable, you'll need to add a statement to the search() method in the model, for example:
public function search()
{
...
$criteria->compare('myField',$this->myField);
...
}
Make sure you have all of those elements present and you shouldn't get the '* is not defined' error.
Also, if you're using schema caching in your main config file, you'll have to clear your cache before the app will see your new database structure.
Your changes should also be set at the Views since there are forms, widgets using the old properties !! (for this exact save issue, you will need to fix _form.php which is the partial responsible from your model Save & Update actions.
You can either do the same as you did with the model: (regenerate it using gii) or you can edit it manually (i recommend you get used to this since in the future you will have code you don't want to loose just because of altering a column name. simple Find & edit in most of the text editors will do the job).
May be you need to read a bit more about how MVC works in general & in Yii in special
This is because you are using schema-cache. Your table schema is cached in Yii. You need to flush AR cache. Either flush full schema cache or use
Yii::app()->db->schema->getTable('tablename', true); in start of your action. This will update model schema-cache.

Categories