I have a request with an authorize method that makes use of:
Illuminate\Http\Request::user()
And it gives me a phpstan error that says
phpstan: Cannot call method hasAnyRole() on App\Models\User|null.
And that makes sense.
So I made an abstract class that makes use of
Illuminate\Http\Request::user()
like this:
<?php
namespace App\Http\Requests;
use App\Models\User;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Http\FormRequest;
abstract class VbpRequest extends FormRequest
{
/**
* Get the user making the request.
*
* #param string|null $guard
* #return User
* #throws AuthenticationException
*/
public function user($guard = null): User
{
$user = parent::user($guard);
if ($user instanceof User) {
return $user;
}
throw new AuthenticationException('Trying to resolve a user, but no one is logged in.');
}
}
In theory this should only return an instance of the User model or an exception.
but when I use this new method it gives me the exact same phpstan warning.
phpstan: Cannot call method hasAnyRole() on App\Models\User|null.
Does anyone have an idea of what is going wrong or could be going wrong?
Here is the Request:
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Models\Role;
use App\Traits\RequestCast;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Validation\Rule;
class BelnotitieRequest extends VbpRequest
{
use RequestCast;
/**
* #throws AuthenticationException
*/
public function authorize(): bool
{
return $this->user()->hasAnyRole(Role::ADMIN_ID, Role::PROJECTMANAGER_ID, Role::ADMINISTRATIEF_MEDEWERKER_ID);
}
}
I'm using Laravel 9.24 and larastan 2.1.12
Related
I try to call "class CltvController" from another class like this :
class StatLtvcController extends AbstractController
{
$cltv_temp = new CltvController();
return $this->render('admin/statltvc.html.twig', [ 'cltv_temp' => $cltv_temp->cltv(), ]);
}
but this class :
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Entity\Game;
use App\Entity\Adventure;
use Symfony\Component\Routing\Annotation\Route;
class CltvController extends AbstractController
{
public function cltv(): float{
$periodh = '2021-06-01'; // $request->request->get('')
$periodi = '2021-07-31'; // $request->request->get('')
$em = $this->getDoctrine()->getManager();
$con = $em->getRepository(Game::class);
$con3 = $em->getRepository(Adventure::class);
$ncnt[] = $con->findByCountncn($periodh,$periodi);
$nadvt[] = $con3->findByCountadv($periodh,$periodi);
return $nadvt[0][0][1]/$ncnt[0][0][1];
}
}
is returning this error :
Call to a member function has() on null
Error
in G:\Mes_TPs\TP_Symfony_PHP\Quaestyo\vendor\symfony\framework-bundle\Controller \AbstractController.php (line 345)
*
* #throws \LogicException If DoctrineBundle is not available
*/
protected function getDoctrine(): ManagerRegistry
{
if (!$this->container->has('doctrine')) {
throw new \LogicException('The DoctrineBundle is not registered in your application. Try running "composer require symfony/orm-pack".');
}
return $this->container->get('doctrine');
}
I don't understand why $this->getDoctrine is returning an error?
Try to add this bloc over CltvController class:
/**
* #method getDoctrine()
*/
class CltvController extends AbstractController {
• If that didn't work, can you tell me the exact version of php & symfony of your project please?
Thanks for your answer. It's the same error message with your solution.Actually, Symfony is calling getDoctrine() in the class StatLtvcController. (i didn't put all because code was too long). How could i call getdoctrine() in the class CltvController ?
I use last versions : PHP 7.4.9 and Symfony 5.2.14
I wanna make API Authentication Laravel Passport - Forgot and Reset Password with this source video on Youtube : https://www.youtube.com/watch?v=F9Xmc3iHc88&t=6s
My source Youtube use Laravel 6x, and i use Laravel 8x.
When i do step "seeding and factory database" in minute video 8:17 , i got
ErrorException array_merge(): Expected parameter 2 to be an array, int given
This is my error cmd
this is my UserFactory.php :
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\User;
class UserFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = User::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'first_name' => $this->faker->firstName,
'last_name' => $this->faker->lastName,
'email' => $this->faker->unique()->safeEmail,
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
];
}
}
this is my UsersTableSeeder.php :
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
User::factory(App\Models\User::class, 10)->create();
}
}
and my DatabaseSeeder.php :
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* #return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
}
}
Can someone help me to explain why i got this error merge array?
Case Closed guys, in Laravel 8x u dont need type "
User::factory(App\Models\User::class, 10)->create();
just type :
User::factory(10)->create();
because u already call it User at first word..
I am new to Laravel. I tried to implement policies in Laravel but I am getting this error:
Call to undefined method App\Providers\AppServiceProvider::registerPolicies()
Anybody can help me how can I resolve it?
Just Use these three things only!
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
Whatever you created about policies in your file AppServiceProvider move it the AuthServiceProvider. The method registerPolicies is exclusive to AuthServiceProvider as you'll see when you open the extended class.
I was wondering if there is a way that i can initialize the property owner with an entity User of FOSUserBundle so that it contains the user who created the Post
I want to do this inside the constructor as shown below.
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post
{
/* here are defined some attributs */
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="posts")
* #ORM\JoinColumn(name="owner", referencedColumnName="id")
*/
private $owner;
public function __construct()
{
$this->owner = /* get current user */ ;
}
}
Is there a way to do this by replacing the comment in the constructor with something ?
Thank you for your answers
No, there isn't. [*]
There are at least two ways to deal with this:
Create your Post entities through a factory service which populates the
owner property:
namespace My\Bundle\EntityFactory;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use My\Bundle\Entity\Post;
class PostFactory
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function createPost()
{
$user = $this->tokenStorage()->getToken()->getUser();
$post = new Post($user);
}
}
(for this example, you will have to modify your Post constructor to
accept the owner as a parameter)
In services.yml:
services:
post_factory:
class: My\Bundle\EntityFactory\PostFactory
arguments: [#security.token_storage]
To create an entity from your controller:
$post = $this->container->get('post_factory')->createPost();
If you can tolerate that the owner will only be set once you persist the
entity, you can use a doctrine event listener:
namespace My\Bundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use My\Bundle\Entity\Post;
class PostOwnerAssignmentListener
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function prePersist(LifecycleEventArgs $event)
{
$entity = $event->getEntity();
if ($entity instanceof Post && !$entity->getOwner()) {
$entity->setOwner($this->tokenStorage->getToken()->getUser());
}
}
}
In services.yml:
services:
post_owner_assignment_listener:
class: My\Bundle\EventListener\PostOwnerAssignmentListener
arguments: [#security.token_storage]
tags:
- { name: doctrine.event_listener, event: prePersit }
The advantage here is that the owner gets assigned no matter how and where
the Post is created.
[*]: Well, technically with the default app.php you could access the
kernel by declaring global $kernel; in your constructor and go from there,
however this is very strongly discouraged and may break in strange and subtle
ways.
I think you are way over-complicating this issue. When you create a new Post in your controller, either in the controller or in the repository do something like this:
use AppBundle\Entity\Post; //at top of controller
$em = $this->getDoctrine()->getManager();
$user = $this->container->get('security.token_storage')->getToken()->getUser();
$post = new Post();
$em->persist( $post );
$post->setOwner( $user );
// set other fields in your post entity
$em->flush();
For Symfony 4+ with Autowiring and Entity event listener:
In /EventListener/PostPrePersistListener.php:
namespace App\EventListener;
use App\Entity\Post;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class PostPrePersistListener
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function prePersist(Post $post, LifecycleEventArgs $event)
{
$post->setOwner($this->tokenStorage->getToken()->getUser());
}
}
In services.yaml:
services:
App\EventListener\PostPrePersistListener:
autowire: true
tags:
- { name: doctrine.orm.entity_listener, entity: 'App\Entity\Post', event: prePersist }
Modifying services.yaml is required as Symfony cannot know that this custom service is tagged to hook on doctrine.event_listener
This works at Entity-level as asked, to ensure Controller do not handle the owner value.
I have the following class. But when I try to access the Yii::app()->user->realName; it generates an error.
I can't understand it all. please help!
Following code is the code of my UserIdentity class.
<?php
/**
* UserIdentity represents the data needed to identity a user.
* It contains the authentication method that checks if the provided
* data can identity the user.
*/
class UserIdentity extends CUserIdentity {
public $id, $dmail, $real_name;
/**
* Authenticates a user.
* The example implementation makes sure if the username and password
* are both 'demo'.
* In practical applications, this should be changed to authenticate
* against some persistent user identity storage (e.g. database).
* #return boolean whether authentication succeeds.
*/
public function authenticate() {
$theUser = User::model()->findByAttributes(array(
'email' => $this->username,
// 'password' => $this->password
));
if ($theUser == null) {
$this->errorCode = self::ERROR_PASSWORD_INVALID;
} else {
$this->id = $theUser->id;
$this->setState('uid', $this->id);
// echo $users->name; exit;
// $this->setState('userName', $theUser->name);
$this->setState("realName",$theUser->fname .' '. $theUser->lname);
$this->errorCode = self::ERROR_NONE;
}
return!$this->errorCode;
}
}
?>
You need to extend the CWebUser class to achieve the results you want.
class WebUser extends CWebUser{
protected $_realName = 'wu_default';
public function getRealName(){
$realName = Yii::app()->user->getState('realName');
return (null!==$realName)?$realName:$this->_realName;
}
public function setRealName($value){
Yii::app()->user->setState('realName', $value);
}
}
You can then assign and recall the realName attribute by using Yii::app()->user->realName.
The protected $_realName is optional, but allows you to define a default value. If you choose not to use it, change the return line of the getRealName method to return $realName.
Place the above class in components/WebUser.php, or anywhere that it will be loaded or autoloaded.
Change your config file to use your new WebUser class and you should be all set.
'components'=>
'user'=>array(
'class'=>'WebUser',
),
...
),