Redirect user to fill attributes if null - php

I am wanting to create a redirect feature as it stands right now my current function in my auth controller is if a film doesn't exist then it redirects to the page to create one. But I am wanting to change this now as this route is no longer needed. What I need instead is if a USER doesn't have an age or gender configured/set it redirects them to the user page /user with the new route name as user_update
Current function:
private function redirect(): RedirectResponse
{
if (!$this->user->film()->exists()) {
return redirect()->route('film_create');
}
return redirect()->route('home');
}
user model: the fields are set as constants with an array of values at the top of the model, e.g types of gender, age range
protected $fillable = [
'id',
'age',
'gender',
];
do you know how i can modify the function above to create an IF statement to check if these fields are set for the user or not if not redirect them to the /user page?
having trouble trying to find resources on how to do this.
I am now writing a test for this function but having a little trouble on doing it..
this is the current test
/**
* #test
* Create mocked user, send them to user page and assert they exists in database
*/
public function it_creates_user_on_redirect()
{
$email = 'foo#bar.com';
$this->mockSocialite($email);
$this->get(route('auth_callback'))
->assertLocation(route('film_create'));
$this->assertDatabaseHas('users', [
'email' => $email,
]);
}
How do i go about modifying this test to ensure that it covers the new function ? #Saly 3301

Should be straightforward
private function redirect(): RedirectResponse
{
if ($this->user->age || $this->user->gender) {
return redirect()->route('home');
}
return redirect()->route('user_update');
}
And here's the boolean logic in tinker
>>> NULL || NULL
=> false
>>> 25 || NULL
=> true
>>> NULL || 'female'
=> true
>>> 25 || 'female'
=> true
Hope this helps

Should be:
private function redirect(): RedirectResponse{
if (!($this->user->age && $this->user->gender)) {
return redirect()->route('user_update');
}
return redirect()->route('home');
}
Only if both fields are configured, you are redirected to 'home'

Related

Laravel timestamps keep track of who has done certain action (like created_at & created_by, updated_at & updated_by)

In my application I want to keep track of who has performed certain operations on different models in my application.
Default Laravel model with timestamps automatically updates fields like created_at and updated_at. I can modify this behavior to set the created_by field automatically by calling the static::updating() function as mentioned in this answer: https://stackoverflow.com/a/64241347/4112883 . This works very well. Additionally, I came across this package (https://github.com/WildsideUK/Laravel-Userstamps), but that is limited to only created, updated, and deleted.
For my Post model, I have more timestamps: created_at, updated_at, completed_at, checked_at, and published_at. When a user ends the post, it must be verified by that user's manager. If all is well, some logic will publish the message, but if not, the manager can create one or more actions for the user to complete the message, which will undo the finishing attributes. An action is created with the following timestamps: created, updated, and completed (null). When the user completes an action, the actions.finished_at and actions.finished_by fields are set.
Now comes the challenge. For each custom timestamp, I want to set the relationship and three functions to handle certain states of the timestamp: set, undo and check for isset:
class Post extends Model
{
//…
public function finishedBy() //relationship belongsTo User::class
{
return $this->belongsTo(User::class, 'finished_by');
}
public function finish() { //function to finish post (SET)
$this->update([
'finished_by' => auth()->id(),
'finished_at' => now(),
]);
}
public function undoFinish() { //function to undo finishing (UNSET)
$this->update([
'finished_at' => null,
'finished_by' => null,
]);
}
public function isFinished() { //function to check if is finished (ISSET)
return !empty($this->finished_by) && !empty($this->finished_at);
}
//…
All four functions must be repeated for ‘checked’ and ‘published’ in the Post model, and for the ‘finished’ attribute in Action model, leading to a lot of almost-duplicate code. (Maybe in the future I want to repeat this logic in other models.)
Is there a possibility to make this more elegant with a Trait or something?
E.g. create something like an protected array $timestamps_with_user by which the application automatically adds the relationship and the three functions?
protected $timestamps_with_users = [
'finish', 'check', 'publish'
];
// foreach in a trait?? Need your help here :D
foreach($timestamps_with_users as $perform) {
public function $perform() { … } //$post->finish()
public function $perform.edBy() :User { … } //$post->finishedBy()
public function undo.$perform() { … } //$post->undoFinish()
public function is.$perform.ed() { … } //$post->isFinished()
}
Thanks in advance and looking forward to your answers.
Just create a new trait and create functions that works with any timestamp:
<?php
namespace App\Traits;
trait CustomTimestamps {
public function perform(string $action)
{
$this->update([
$action . 'ed_by' => auth()->id(),
$action . 'ed_at' => now(),
]);
}
public function undo(string $action)
{
$this->update([
$action . 'ed_by' => null,
$action . 'ed_at' => null,
]);
}
public function check(string $action)
{
$at = $action . 'ed_at';
$by = $action . 'ed_by';
return !empty($this->{$by}) && !empty($this->{$at});
}
}

CodeIgniter 4 - Validation Custom Rule Function Quandry

In my CI4 learning, I have started by trying to simulate user sign in functionality. I have a Controller, two Views (not shown here, but really simply pages- one a pretty much just single form, and the other one a “blank” success HTML page), a set of custom rules in the Validation.php file, and a CustomRule.php file with the first of the methods that will implement all my custom rules (which, ultimately, I’d like to have all set in the Validation.php file). For lack of a better idea, I’ve stuck the CustomRules.php file in the app\Config\ folder.
Here is my problem:
For the life of me, I can’t figure out how to get the Validation service to pass additional parameters (from the form) to my custom rules function called ‘user_validated’. The CI4 documentation describes what the custom function needs to cater for when accepting additional parameters, but not how to trigger the Validation service to pass these additional parameters to one’s custom function… so although ‘user_validated’ is called, only ‘user_email_offered’ is ever passed as in as a string- nothing else goes in, from what I can tell. How do I get around this?
I have tried inserting < $validation->setRuleGroup('user_signin'); > before the call to validate, but found that I could move the setting of the rule group into the call to validate, using: $validationResult = $this->validate('user_signin'), which seemed to do the same, and which doesn't seem to work without the rule-group as a parameter (?). This still doesn't seem to be what triggers the additional data to be passed to the custom rule's method.
Extracts from my hack are appended below.
I’d be very grateful one of you knowledgeable folk could please point me in the right direction.
In app\Controllers\SignupTest.php:
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
class SignupTest extends BaseController
{
public function index() { // redirection from the default to signup(), signin(), ...
return $this->signup();
}
public function signup() {
helper(['form']);
$validation = \Config\Services::validation();
if ($this->request->getPost()) { // still TBD: any different to using $this->request->getGetPost() ?
$validationResult = $this->validate('user_signin'); // set the rules to use: 'user_signin', 'user_signup'
if (!$validationResult) {
$validationErrors = $validation->getErrors();
return view('SignupTestView', $validationErrors); // redisplay simple html form view with list of validation errors
} else {
return view('SignupTestViewSuccess'); // display view to show success
}
} else {
return view('SignupTestView'); // initial display, in the event of there being no POST data
}
}
}
In \app\Config\CustomRules.php:
<?php
namespace Config;
use App\Models\UserModel;
//--------------------------------------------------------------------
// Custom Rule Functions
//--------------------------------------------------------------------
class CustomRules
{
public function user_validated(string $str, string $fields = NULL, array $data = NULL, string &$error = NULL) : bool{
$user_email_offered = $str;
$user_password_offered = ''; // to be extracted using $fields = explode(',', $fields), but $fields is never provided in the call to this user_validated method
if (($user_email_offered !== NULL) && ($user_password_offered !== NULL)) {
$usermodel = new UserModel(); // intended to create a UserEntity to permit connectivity to the database
$user_found = $usermodel->find($user_email_offered); // we're going to assume that user_email is unique (which is a rule configured in the database table)
if ($user_found === NULL) { // check if user exists before doing the more involved checks in the else-if section below, which may throw exceptions if there's nothing to compare (?)
...
}
}
In \app\Config\Validation.php:
?php
namespace Config;
class Validation
{
//--------------------------------------------------------------------
// Setup
//--------------------------------------------------------------------
/**
* Stores the classes that contain the
* rules that are available.
*
* #var array
*/
public $ruleSets = [
\CodeIgniter\Validation\Rules::class,
\CodeIgniter\Validation\FormatRules::class,
\CodeIgniter\Validation\FileRules::class,
\CodeIgniter\Validation\CreditCardRules::class,
\Config\CustomRules::class,
];
/**
* Specifies the views that are used to display the
* errors.
*
* #var array
*/
public $templates = [
'list' => 'CodeIgniter\Validation\Views\list',
'single' => 'CodeIgniter\Validation\Views\single',
];
//--------------------------------------------------------------------
// Custom Rules
//--------------------------------------------------------------------
/* configurable limits for validation rules array below*/
const user_email_min_lenth = 9;
const user_email_max_lenth = 50;
const user_password_min_lenth = 6;
const user_password_max_lenth = 25;
public $user_signin = [
'user_email' => [
'label' => 'e-mail address',
'rules' => 'trim|required|valid_email|user_validated', // user_validated is custom rule, that will have a custom error message
'errors' => [
'required' => 'You must provide an {field}',
'valid_email' => 'Please enter a valid {field}',
]
],
'user_password' => [
'label' => 'password',
'rules' => 'trim|required',
'errors' => [
'required' => 'Enter a {field} to sign in',
'user_password_check' => 'No such user/{field} combination found',
]
Calling custom rule with parameters should be exactly the same as calling CI4's regular rules. Let's get for example "required_without". You use it like in this example:
$validation->setRule('username', 'Username', 'required_without[id,email]');
And the function is declared as so:
public function required_without($str = null, string $fields, array $data): bool
{
$fields = explode(',', $fields);
//...
}
where $str - this is your main field, $fields - string, packing a comma-separated array.
As for Grouping rules, you do not need to group rules to be able to use custom rules with parameters.
If you have only 2 fields to test against you can go a bit cheaper, which will not be perfect but still works:
Function:
public function myrule(string $mainfield, string $fieldtotestwith): bool
{
//doing stuff
}
Validating rule:
$validation->setRule('somemainfield', 'Something', 'myrule[somesecondfield]');

Can we pass null to laravel resource

I have InitResource that return basic data like some app data like app name etc..
Now if user is logged in, I return resource with user data or without user data, but when the user is null or auth()->user() is null, I get some error:
Cant return id of null
public function __get($key)
{
return $this->resource->{$key};
}
And when user is logged it works like it should.
Logically I am trying to return resource without data, but can I use it with some "default values" ?
In resource I am checking:
$data = [
'appName' => config('app.name'),
'locale' => app()->getLocale(),
];
if($this->hasRole('basic')) {
$data['user'] = $this->only(['username', 'fullname', 'avatar']);
}
return $data;
Can this be done in resource? With passed null? Thanks

Laravel Validations where one filed is unique where other filed must have some id

I want to pass $params['user_id'] to $fieldValidations and check if the hour is unique for specific user_id not for all hours hour in the database table
I created a model post
class Post extends Model
{
protected $fillable = ['user_id', 'hour'];
public static $fieldValidations = [
'user_id' => 'required',
'hour' => 'required|date_format:Y-m-d H:i:s|unique:post,hour,NULL,user_id,'
];
}
and a controller post
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$params = $request->all();
$params['user_id'] = 12;
$validator = Validator::make($params, Post::$fieldValidations);
if ($validator->fails()) {
return Response::json($validator->errors()->all(), 422);
}
}
}
I don't think you can do this using the unique validation rule. From Laravel 5.7 documentation:
The field under validation must be unique in a given database table.
Note it says table and not column.
You may have to just query the database and return a JSON response error if it fails. Also, in your current code inside the validation rules, you are specifying that user_id is the primary id key column in the post table. I think that is likely an error and should be removed, even though it's irrelevant given that you can't accomplish what you want using the unique rule. Also, you ended the rule with a comma.
if (Post::where(['user_id' => $params['user_id'], 'hour' => $params['hour']])->exists()) {
return response()->json(['status' => 'error', 'msg' => 'Error', 'errors' => ['hour_error' => ['That hour already exists on the user!']]], 422);
}
Lastly, instead of using $params = $request->all(), I prefer to use the request() helper function and just inline it into the rest of the code. But, that's up to you.

Laravel policy for editing

So I have created a policy and registered it in the AuthServicePRovider, but it always returns false. It is my first time working with policies so I am sure I am doing it wrong, but following a few examples, nothing has worked for me.
I am logged in with user that has an id of 1. I try to edit a label that has a user_id of 1, returns false, and also when trying to edit a label that has a user_id of 2. This last one works as expected, but f the user_id and label->user_id match, I should ave a form displayed. Instead, I get this each time:
This action is unauthorized.
Any ideas?
AuthServiceProvider: (Tried both but both don't work):
protected $policies = [
'App\Label' => 'App\Policies\LabelPolicy'
];
And this one also did not do the trick:
protected $policies = [
Label::class => LabelPolicy::class
];
LabelsController#edit:
public function edit(Label $label)
{
// $this->authorize('edit', $label); // This also returns false
if (auth()->user()->cannot('edit', $label)) {
dd('NO'); // This is always shown
}
}
LabelPolicy:
public function edit(Label $label)
{
dd('test'); // This is never shown anywhere
return auth()->user()->id === $label->user_id;
}
The policies expects actually two inputs, the first input is always the User class, the second input is the Model and defaults to the Model class. So in your case:
LabelPolicy
public function edit(User $user, Label $label)
{
return $user->id === $label->user_id;
}
LabelsController#edit:
public function edit(Label $label)
{
$this->authorize($label);
}
if your $this->authorize inside Controllers always returns false, then double check if your model and model policy namespace was imported in AuthServiceProvider and also your model has been imported into your controller.

Categories