validation rule unique in laravel - laravel

I want to do a validation of two fields, I have tried but it does not work for me, exactly what I need is for two fields to be validated.
what I want is that the namames of fields "grado" and "fk_nivel_academico" are not repeated
This is the table in my database:
-----------------------------------------------
id | grado | fk_nivel_academico | fk_estado
1 | Primero | 1 | 1
2 | Segundo | 1 | 2
This is the validation rule:
$validatedData = Validator::make(
[
'grado' => $this->grado
],
[
'grado' => [
'required',
Rule::unique('grado')
->where('fk_nivel_academico', '==', $this->fk_nivel_academico)
->where('grado', '==', $this->grado)
]
],

I think the problem it's that you are using the == comparator when in sql it is =.
The other solution I would recommend you to use, is to make a Rule file.
Here's a guide How to create custom validation
And in the passes method you could use something like this
public function passes($attribute, $value)
{
$table = Table::where('fk_nivel_academico', '=', $this->fk_nivel_academico)
->where('grado', '=', $this->grado)->get();
if($table->count() == 0){
return true;
}
}

Related

Laravel model observer method doesn't get fired

In my Laravel (7.x) I am trying to use Observers for updating device_inventories table if the value of the record if the record of the subscriber_devices table is updated as inactive.
device_inventories | subscriber_devices
----------------------- | --------------------------------
# | title | status | # | device_inventory_id | status
----------------------- | --------------------------------
1 | device-1 | active | 1 | 1 | active
2 | device-2 | active | 2 | 2 | active
For example:
Suppose I update the record id = 2 of subscriber_devices to status = inactive then the value of device_inventories / status should be updated to replacement. Assuming, that the device was damaged and needs to be sent for replacement.
AppServiceProvider.php
use App\SubscriberDevice;
use App\Observers\ObserverChangeDeviceInventoryStatusToReplacement;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
SubscriberDevice::observe(ObserverChangeDeviceInventoryStatusToReplacement::class);
}
}
App\Observers\ObserverChangeDeviceInventoryStatusToReplacement.php
use App\DeviceInventory;
use App\SubscriberDevice;
class ObserverChangeDeviceInventoryStatusToReplacement
{
public function updated(SubscriberDevice $SubscriberDevice)
{
DeviceInventory::where('id', $SubscriberDevice->device_inventory_id)->update([
'status' => 'replacement'
]);
}
}
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
self::where('token', $token)->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
The observer method doesn't seems to be firing. What am I doing wrong.?
Thanks to #EmptyBrain.
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
$self = self::where('token', $token)->first();
$self->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
Reference laravel Eloquent model update event is not fired

Exists validations with multiple tables

Laravel version (7.x).
I have three tables companies, complaint_types & complaints.
The complaints are associated with a company.
When a complaint is assigned to an employee then only he/she can see and enter the complaint status after the visit. Before entering the comments the complaint must be validated, otherwise the comment must not be allowed to enter, which is handled upon submitting the form via a hidden field called complaint_id.
I have added this validation because I don't want anyone opening the inspect tool, playing with the values and causing the application an error.
Tables:
companies: -> complaint_types: -> complaints:
|-- id |-- id |-- id
|-- ... |-- company_id |-- complaint_type_id
|-- ... |-- ...
Complaint.php:
public function complaintType()
{
return $this->belongsTo(ComplaintType::class);
}
ComplaintType.php:
public function company()
{
return $this->belongsTo(Company::class);
}
public function complaints()
{
return $this->hasMany(Complaint::class);
}
ComplaintController.php:
private function validate($data)
{
# variables
$rules = [
'complaint_id' => [
'required',
'exists:complaints,id,complaintType.company_id,' . session()->get('COMPANY')
],
-- OR --
'complaint_id' => [
'required',
'exists:complaints,id,complaint_types.company_id,' . session()->get('COMPANY')
],
...
];
$messages = [
'complaint_id.required' => '`Complaint` - Required<br>',
'complaint_id.exists' => '`Complaint` could not be identifed<br>',
...
];
return Validator::make($data, $rules, $messages);
}
Error
Column not found: 1054 Unknown column 'complaintType.company_id' in 'where clause' (SQL: SELECT COUNT(*) as aggregate FROM complaints WHERE id = 3 AND complaintType.company_id = 1)
Problem solved, Here is my code:
$rules = [
'complaint_id' => [
'required',
function($attribute, $value, $fail) use ($data) {
$Complaint = Complaint::where('id', $value)
->whereHas('complaintType', function($query) use($data) {
return $query->where('company_id', session()->get('COMPANY'));
})->count();
if($Complaint <= 0)
$fail('`Complaint` could not be identified');
}
],
I found the reference here How can I add a join to a custom exists rule for a laravel validator?
You should specify relationship with primary key like:
public function complaints(){
return $this->hasMany(Complaint::class, 'complaint_type_id','id');
}

How to validate 2 Laravel fields in a NAND way

I'm looking for a solution to the following case: my controller action receives 2 variables, however they must exclude eachother. So you can pass either 1 of them or none.
The following truth table arises (NAND):
| A | B | Result |
|---|---|--------|
| 0 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
(say A equals points and B equals coupon).
// MyController.php
$this->validate($request, [
'points' => '',
'coupon' => ''
]);
I've tried some solutions like nullable|required_without:field_name but those seem to result in an XOR, which means you must pass at least 1 of them.
I've decided to create a new rule called without:field1,field2:
Validator::extend('without', function($attribute, $value, $parameters, \Illuminate\Validation\Validator $validator) {
foreach($parameters as $compare) {
// Check if the given parameters are filled in, if so we return `false`.
if($validator->validateRequired(null, array_get($validator->getData(), $compare))) {
return false;
}
}
// No `without` parameters have been found, validation successful.
return true;
});
// Replace the error message placeholders.
Validator::replacer('without', function ($message, $attribute, $rule, $parameters, \Illuminate\Validation\Validator $validator) {
$attributes = [];
foreach ($parameters as $key => $value) {
$attributes[$key] = $validator->getDisplayableAttribute($value);
}
return str_replace(':values', implode(' / ', $attributes), $message);
});
Next, add a translation to resources/lang/en/validation.php:
'without' => 'The :attribute may not be set with :values',
Then you can simply use it like this:
$this->validate($request, [
'points' => 'nullable|without:coupon',
'coupon' => 'nullable|without:points'
]);
(you may add multiple parameters like without:coupon,user_id)
Which makes sure that:
Passing pointsand coupon together results in a validation error
Passing a single one of them is OK
Passing neither of them is OK

How use conditional relationship in eloquent laravel

I have a 'conversation_message' table and separate sender role by column 'sender_type' (admin/user). Both of admin & user in a different table. But when I call the model, that showed error Call to a member function addEagerConstraints() on null
Table column and data
| id | id_group | id_reply | id_sender | sender_type | message
| 1 | 1 | null | 3 | admin | Hi, I'm admin
| 2 | 1 | 1 | 3 | admin | I wanna give u promo
| 3 | 1 | 2 | 18 | user | What's promo ?
I've tried if conditional with column value, but it doesn't work.
Conversation_message.php
public function sender(){
switch($this->sender_type){
case "user":
return $this->belongsTo(User::class, 'id_sender', 'id');
case "admin":
return $this->belongsTo(Admin::class, 'id_sender', 'id');
default:
return;
}
}
InboxController.php
public function message_detail_chat($groupId){
$data = [];
$data['messages'] = Conversation_message::with('replies')
->with('sender')
->with('recipients')
->where(['id_group' => $groupId])->get();
}
I expect to use conditional model by column value 'sender_type' but the actual output is wrong.
laravel provide query scope your can read it from here https://laravel.com/docs/5.8/eloquent#local-scopes
function userSender(){
$this->belongsTo(User::class, 'id_sender', 'id');
}
function adminSender(){
return $this->belongsTo(Admin::class, 'id_sender', 'id');
}
public function scopeSender($query)
{
return $query
->when($this->sender_type === 'user',function($q){
return $q->with('userSender');
})
->when($this->sender_type === 'admin',function($q){
return $q->with('adminSender');
});
}
Now you can access your Sender like this
Conversation_message::Sender()->first();
This should give you the right Sender. Hope it helps.

Updating a Whole Table (Laravel and Vuejs)

I have a table on admin panel which contains 60 rows and 10 columns. Those columns belongs 2 different tables in database. Simplified version is like so:
| Asset | Trade | Status | Code | Link |
Code and Link columns contain empty text inputs. When I enter anything, their values go to Vue.js object and I save them in the Posts table which works perfectly fine.
Trade and Status columns have radio buttons and they belong to Trades table. When I check the radio buttons, their values go to Vue.js object. When I try to update the Trades table it returns 405 error.
The button calls that function:
publish() {
this.form.post("/api/posts")
.then(() => {
this.form
.put("/api/trades")
})
.catch(() => {
//error codes...
});
}
I don't know if POST and PUT requests can be used together, or if this is the correct syntax, but I like to make both action with one click.
My update function is down below. As you can see I'm trying to use "asset_id" which is the foreign key in the Trades table, instead of "id" which is primary key of Trades table. I think this causes the problem, but I'm not sure. I also add that line of code to Post modal file, but it didn't help:
protected $primaryKey = 'asset_id';
My update function:
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $asset_id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $asset_id)
{
$input = $request->all();
if (count($input['asset_id']) > 0) {
foreach ($input['asset_id'] as $asset_id) {
if (array_key_exists($asset_id, $input['status'])) {
$updates = [
'trade' => $input['trade'][$asset_id],
'status' => $input['status'][$asset_id],
'private' => (array_key_exists($asset_id, $input['public_code']) ? 0 : 1)
];
Post::where('asset_id', $asset_id)->find($asset_id)->update($updates);
}
}
}
}
When I check the radio buttons and fill the inputs Vue.js object becomes something like this:
form: new Form([
asset_id: [2, 5, 16, 52],
trade: {
2: "Long",
5: "None",
16: "Long",
52: "Short"
},
status: {
2: "Active",
5: "Canceled",
16: "Pending"
52: "Active"
},
public_code: {
2: "VBFABVR",
16: "hmbtr46"
},
link: {
2: "http://...",
16: : "http://..."
}
])
I'm not sure if I should provide more details. If it's so, please let me know. I hope someone tells me what is wrong here.
EDIT:
Route codes:
Route::apiResources([
'users' => 'API\UsersController',
'roles' => 'API\RolesController',
'plans' => 'API\PlansController',
'categories' => 'API\CategoriesController',
'assets' => 'API\AssetsController',
'posts' => 'API\PostsController',
'trades' => 'API\TradesController',
]);
Route List:
| POST | api/trades | trades.store | App\Http\Controllers\API\TradesController#store | api
| GET|HEAD | api/trades | trades.index | App\Http\Controllers\API\TradesController#index | api
| PUT|PATCH | api/trades/{trade} | trades.update | App\Http\Controllers\API\TradesController#update | api
| GET|HEAD | api/trades/{trade} | trades.show | App\Http\Controllers\API\TradesController#show | api
| DELETE | api/trades/{trade} | trades.destroy | App\Http\Controllers\API\TradesController#destroy | api
It was a rookie mistake. I was trying to pass a collection of the trades to database, but put request requires /api/trades/$id. That's why I've created a for loop in front-end, and in back-end, all I need was a simple update function.
This is the function that is called when I click the submit button:
publish() {
var trades = this.form.asset_id.length;
this.form.post("/api/posts")
for (var i = 0; i < trades; i++) {
if (this.form.asset_id[i] in this.form.status) {
this.form.put("/api/trades/" + this.form.asset_id[i])
}
}
}
And this is the upload function:
public function update(Request $request, $id)
{
$item = Trade::findOrFail($id);
$input = $request->all();
$updates = [
'trade' => $input['trade'][$id],
'status' => $input['status'][$id],
'private' => (array_key_exists($id, $input['public_code']) ? 0 : 1)
];
$item->update($updates);
}

Resources