How to validate 2 Laravel fields in a NAND way - laravel

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

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

validation rule unique in 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;
}
}

Opposite of required_if in laravel using multiple value evaluation

How to validate must null if another field has specific value or not null
In my case it is the opposite of required_if with multiple values
$rule = array(
'selection' => 'required',
'stext' => 'required_if:selection,2|required_if:selection,3',// stext should be null if selection is 2 or 3
);
And if needed how to perform own validation?
So in your example you can do something like this:
$rule = array(
'selection' => 'required',
'stext' => 'required'
);
// override the rule
if(in_array(request('selection'), [2, 3]))
{
$rule['stext'] = 'nullable';
}
This means if the selection is 2 the field will be required and if the selection field has any other value the stext field will be required.
I am not sure if I understood your question correctly. In any case the opposite of required_if is required_without so you can use that one if you want this field to be required even if the selection is empty.
With custom rule your passes method should look like this:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CustomRule implements Rule
{
protected $selection;
public __construct($selection)
{
$this->selection = $selection;
}
public function passes($attribute, $value)
{
return $value === null && in_array($this->selection, [2, 3]);
}
}
You use it like this:
$rule['stext'] = [ new CustomRule(request('selection') ]
I try to extend the validation rule. put the following in AppServiceProvider:
Validator::extend('null_if', function ($attribute, $value, $parameters, $validator) {
$other = $parameters[0];
$other_value = array_get(request()->toArray(), $other);
if ($parameters[1] == $other_value) {
return empty($value);
}
return true;
});
Tell me if it's work or what error given to you.

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