How to remove item from request class Laravel - php

I want to remove slug from $request when I'm updating record and new slug equals to old slug! (Because I use $request->validate() to check if slug is unique in table but it fails!)

You can do that without removing the field. For example, if you're using validation request, you can ignore the unique-checking like this:
// don't forget to use this in the top of your class-file
use Illuminate\Validation\Rule;
public function rules()
{
// for your case replace the word "post"s if need for your appropriate table
return [
'slug' => ['required', Rule::unique('posts')->ignore($this->post()->id)]
];
}

I found the solution! we can use $requst->requst->remove('slug');

Related

How to validate multiple fields selectively using a common class in PHP laravel while returning all errors

Just started with my first PHP laravel project, and I have this
UserRepository.php
public function validateNewEmail(Request $request, $emailName) {
$request->validate([
$emailName => ['required', 'email', 'unique:users'],
]);
}
public function validateNewPassword(Request $request, $passwordName) {
$request->validate(
// rule
[
$passwordName => ['required', 'min:8',
'regex: // some long long regex'
],
// message
[
$passwordName.".regex" => "Your new password must be more than 8 characters long, should contain at-least 1 uppercase, 1 lowercase, 1 numeric and 1 special character.",
]
);
}
// Usr Id is by itself as it might be optional in registering.
public function validateNewUsrId(Request $request, $userIdName) {
$request->validate([
$userIdName => 'required|unique:users',
]);
}
And I can then use this repository easily like this in my controller.
$this->userRepository->validateNewEmail($request, "email");
$this->userRepository->validateNewPassword($request, "password");
$this->userRepository->validateNewUsrId($request, "usr_id");
The reason is because there might be multiple controllers that use the same rules, thus putting them in one place is better
However, I realised that this method does not work because it returns the first error only. For example when both the email and password is wrong, only the email error gets returned to the frontend.
What is the best way to achieve what I want? I want to put all my validation/rules in one place to be reused.
My first solution is this: Each function returns the error MessageBag which is then joined together. I will use return $validator->errors(); to return the MessageBag which can be found here https://laravel.com/docs/8.x/validation#retrieving-the-first-error-message-for-a-field
However I slightly dislike it because then in order to check for whether an error occured, I would need to check if MessageBag is empty and then throw an error which seems a little weird.
The other way I thought of is to return the rules instead, so that I can join them in my controller and then validate all in one go. This seems better but I have to combine the error messages as well, which can be a little tricky if there are multiple (since the key is not fixed, see the code $passwordName.".regex" for example.), as I will need to update the key to each message.
The best way for me, is if I could return a validator for each function, and then use some sort of Validate::ValidateAll function? Is that possible?
How is this implemented usually?
Modern Laravel applications typically use form request validation. With this approach, Laravel handles all the validation and returns error messages automatically. Simply write your form request class, and then use it in place of Request in your controller methods:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
class MyFormRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'email' => ['required', 'email', 'unique:users'],
'password' => [
'required',
Password::min(8)->mixedCase()->numbers()->symbols()
],
'usrid' => ['required', 'unique:users'],
];
}
}
public method store(MyFormRequest $request)
{
// $request has been validated, no further checking needed
}
Note I'm using Laravel's built-in password validation rules instead of relying on a "long long regex."

Laravel route redirecting with data

I have a basic route that looks like this:
Route::prefix('/group')->group(function () {
// Some routes here
Route::prefix('/{uuid}')->group(function () {
// Some routes here
Route::get('/user/{id}', 'Controller#preview')->name('view-user')->where('id', '[0-9]+');
}
}
The logic is that I want the id to be only numerical value. What I want to do now is, to declare a redirection to this, if the value is non-numerical. Let's say the input of id is fs. In that case I would want it to redirect to id with value 1.
I tried using Route:redirect, but could not make it work. It looks something like this:
Route::redirect('/group/{uuid}/user/{id}', '/group/{uuid}/user/1')->where('id', '[^0-9]+');
I would prefer to put the redirect inside the groups, but it can be outside if this is the only way. Any help will be greatly appreciated.
What happens is, that I get a 404 error if I have the route redirect declared.
EDIT: I want to do it in the routes/web.php file. I know how to do it in the controller, but it is not what I need in the current case.
Closures are not an option either, because that would prevent routes caching.
Following up on the comment
You can create a Route in routes/web.php file that catches non-digit ids and redirect this to 'view-user' with id=1
It would look something like this
Route::get('/group/{uuid}/user/{id}', function ($uuid, $id) {
return redirect('view-user', ['uuid' => $uuid, 'id' => 1]);
})->where('id', '[^0-9]+');
// and then below have your normal route
Route::get('/group/{uuid}/user/{id}', 'Controller#preview')->name('view-user')->where('id', '[0-9]+');
Update
Following you comment that you do not want to use closures.
Change the "bad input route" to
Route::get('/group/{uuid}/user/{id}', 'Controller#redirectBadInput')->where('id', '[^0-9]+');
and then add the method in class Controller:
public function redirectBadInput ($uuid, $id) {
return redirect('view-user', ['uuid' => $uuid, 'id' => 1]);
}
You can see more in this SO thread about redirects and caching.
You declared it inverted.
In Laravel you can redirect passing parameters in this way:
You can pass the name instead of the url and simply pass variables.
Redirect::route('view-user', [$uuid, $id])
I think that you can do it inside of the controller of the router, with a logic like this:
class Controller {
// previous code ..
public function preview($uuid, $id) {
if(! is_numeric($id))
return redirect("/my-url/1");
// run the code below if $id is a numeric value..
// if not, return to some url with the id = 1
}
}
I think that there is no way to override the 'where' function of laravel, but I guess that have something like that in Routing Bindings:
Alternatively, you may override the resolveRouteBinding method on your Eloquent model. This method will receive the value of the URI segment and should return the instance of the class that should be injected into the route:
/**
* Retrieve the model for a bound value.
*
* #param mixed $value
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value)
{
return $this->where('name', $value)->first() ?? abort(404);
}
But it's require that you manage consise model's values instead of ids of whatever you want.
assign route name in route as like.
return Redirect::route('view-user', ['uuid'=>$uuid,'id'=>$id]);
As you want in web.php file then.
Route::get('/group/{uuid}/user/{id}', function($uuid, $id){
echo $uuid;
echo $id;
})->name('view-user')->where('id', '[0-9]+');

How can I add function name as route in Laravel 5.7?

I have a controller which returns enums for respective fields. e.g.
// Expected route - /api/getFamilyTypes - only GET method is allowed
public function getFamilyTypes()
{
return [
'Nuclear Family',
'Joint Family'
];
}
I've around 20 functions like this. How can I add this without manually adding an entry per function in routes file?
Thanks in advance.
In your routes file, add something like this,
Route::get('/something/{func}', 'SomeController#functionRoute');
Where something is whatever path you're wanting to use and SomeController is the controller with the 20 functions you're using and functionRoute is the action that we're about to make.
Then in your controller, make a function like this,
public function functionRoute($func)
{
return $this->$func();
}
This will make it so that whenever someone browses to /something/* on your website, it'll execute the function name at the end. So if you navigate to /something/getFamilyTypes it'll run your getFamilyTypes function.
This isn't particularly secure. If you do this, the user will be able to run any of the controller's methods. You could set up a blacklist like this.
public function functionRoute($func)
{
$blacklist = [
'secret',
'stuff',
];
return in_array($func, $blacklist) ? redirect('/') : $this->$func();
}
Or you could set up a whitelist like this,
public function functionRoute($func)
{
$whitelist = [
'getFamilyTypes',
'otherUserFriendlyStuff',
];
return in_array($func, $whitelist) ? $this->$func() : redirect('/');
}
If the responses are always from hard-coded arrays (as opposed to being from a database) then one way might be to have a variable in your route:
Route::get('/api/enum/{field}', 'EnumController#getField');
And then in your controller method, use the variable to get the correct data from a keyed array:
public function getField($field)
{
$fields = [
'family' => [
'Nuclear Family',
'Joint Family'
],
// ...
];
return $fields[$field];
}
If you want to continue using different methods for every field then Michael's answer is the easiest option, with one caveat. Allowing users to call any method by name on your controller is a security risk. To protect yourself, you should validate the method name against a whitelist.

Laravel 5.5: 'Auto increment' unique field if exists in database

I have a database table called short_urls which has a list of urls and their 'short' path.
The main fields to note are:
$table->string('url')->unique();
$table->string('full_path');
I wanted to know if there is a way in Laravel 5.5 to automatically add a number to the url field if it already exists.
For example, if I was to use a DatabaseSeeder to populate the database and create a ModelFactory for ShortUrl, something like:
$factory->define(App\Models\Route::class, function (Faker $faker) {
return [
'url' => $faker->slug,
'full_path' => $faker->url
]
});
And in my DatabaseSeeder I did something like:
$shortUrl1 = factory(ShortUrl::class)->create([
'url' => 'my-short-url'
])
$shortUrl2 = factory(ShortUrl::class)->create([
'url' => 'my-short-url'
])
I want to know if there was a way to ensure that when the second ShortUrl that gets created ($shortUrl2)...
...It can automatically detect that if a record in the database already exists with the url it is trying to set...
...Then it should set it to something like url => 'my-short-url-2'
Thanks
What you can do is enforce a check before inserting the url into database, so that we can know if the url is a duplicate or not then append a number accordingly, here is a sample
Then from the controller you can call
private function getUniqueUrl($url)
{
$slug = str_slug(trim($url), '-');
$existingCount = Url::where('url', 'like', $slug . '-%')->count();
if($existingCount)
{
return $slug . '-' . ($existingCount);
}
return $slug
}
use the $slug to insert into database;

Yii2 elasticsearch setAttributes() not working

I am using Yii 2.1 and Yii2-elasticsearch 2.0.3 with a elasticsearch 1.5.0 server to try and index a Member model for a more powerful search. I have a common\indexes\Member model that extends yii\elasticsearch\ActiveRecord and set up the attributes I want to index.
namespace common\indexes;
use yii\elasticsearch\ActiveRecord;
class Member extends ActiveRecord {
/**
* #return array the list of attributes for this record
*/
public function attributes() {
// path mapping for '_id' is setup to field 'id'
return ['id', 'given_name', 'family_name', 'email'];
}
}
I am having trouble setting the attributes I want in the common\indexes\Member model.
I am creating a new instance of the object and trying to set the attribute values via the ActiveRecord setAttributes() method but it doesn't seem to set any values.
$index = new common\indexes\Member();
$index->setAttributes(['given_name' => 'Test', 'family_name' => 'User', 'email' => 'test.member#test.com']);
$index->save();
This seems to create an empty record. If I manually set the attributes one by one everything seems to work and a record with the correct attributes is created in the elasticsearch database.
$index = new common\indexes\Member();
$index->given_name = 'Test';
$index->family_name = 'User';
$index->email = 'test.member#test.com';
$index->save();
Am I using the setAttributes() method incorrectly for elasticsearch ActiveRecord's? Do I need to set up my elasticsearch Model differently?
By default setAttributes only sets attributes that have at least a single validation rule defined or those that are - as a minimum - defined as being "safe" (either via safeAttributes() or via the safe-validator).
You can force it to assign everything by just changing the call to
$index->setAttributes([
'given_name' => 'Test',
'family_name' => 'User',
'email' => 'test.member#test.com'
], false);
This tells it it is okay to also assign non-safe attributes.
But I usually prefer to make sure the validation is configured correctly

Categories