Reputation: 357
I have a small app & I'm using Laravel 11 and I try to expose only the UILD and not IDs and I got "Call to undefined method App\Models\User::assignRole()"
User.php
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles; // Ensure this is imported
use Illuminate\Support\Str;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\DB;
class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, HasFactory, Notifiable, HasRoles;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'name',
'email',
'password',
'ulid',
'store_id',
'is_active',
'wpuserId',
'phone',
'avatar_url',
'description',
];
/**
* The attributes that should be hidden for serialization.
*/
protected $hidden = [
'id',
'ulid',
'password',
'remember_token',
'email_verified_at',
];
/**
* The attributes that should be cast to native types.
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'is_active' => 'boolean',
];
/**
* Boot the model.
*/
protected static function boot()
{
parent::boot();
static::creating(function ($user) {
if (empty($user->ulid)) {
$user->ulid = Str::ulid()->toBase32(); // Automatically generate a ULID
}
});
}
/**
* Relationship with UserProvider.
*/
public function userProviders(): HasMany
{
return $this->hasMany(UserProvider::class);
}
/**
* Relationship with Store.
*/
public function store(): BelongsTo
{
return $this->belongsTo(Store::class);
}
/**
* Assign roles to the user and update `model_has_roles` with ULID.
*/
public function assignRole(...$roles): static
{
\Log::info('User.php > Inside assignRole method', ['roles' => $roles, 'user_id' => $this->id]);
// Log the roles being assigned
\Log::info('User.php > Assigning roles to user', [
'user_ulid' => $this->ulid,
'user_id' => $this->id,
'roles' => $roles,
]);
try {
// Call the Spatie method to assign roles
\Log::info('User.php > Inside try roles', $roles);
parent::assignRole(...$roles);
// Log after the parent method call
\Log::info('User.php > Parent assignRole method executed', [
'user_id' => $this->id,
]);
// Update the ULID in the model_has_roles table for each role
foreach ($roles as $role) {
\Log::info('User.php > Processing role', [
'role_name' => $role,
]);
$roleModel = Role::where('name', $role)->first();
if ($roleModel) {
\Log::info('User.php > Role found', [
'role_id' => $roleModel->id,
]);
$updatedRows = DB::table('model_has_roles')
->where('role_id', $roleModel->id)
->where('model_id', $this->id)
->update([
'model_ulid' => $this->ulid,
'updated_at' => now(),
]);
\Log::info('User.php > model_has_roles table updated', [
'role_id' => $roleModel->id,
'model_id' => $this->id,
'updated_rows' => $updatedRows,
]);
} else {
\Log::warning('User.php > Role not found', [
'role_name' => $role,
]);
}
}
return $this;
} catch (\Exception $e) {
// Log the exception if something goes wrong
\Log::error('User.php > Error assigning roles', [
'error_message' => $e->getMessage(),
'user_id' => $this->id,
]);
throw $e; // Optionally re-throw the exception
}
}
/**
* Relationship with roles.
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class, 'model_has_roles', 'model_id', 'role_id')
->wherePivot('model_type', self::class)
->withPivot('model_ulid'); // Include ULID in the relationship
}
/**
* Activate the user.
*/
public function activate(): void
{
$this->is_active = true;
$this->save();
}
/**
* Deactivate the user.
*/
public function deactivate(): void
{
$this->is_active = false;
$this->save();
}
}
Role.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Spatie\Permission\Models\Role as SpatieRole;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class Role extends SpatieRole
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'guard_name',
'ulid', // Add ULID to fillable fields
];
/**
* The attributes that should be cast to native types.
*
* @var array<string, string>
*/
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* Automatically generate ULID for roles on creation.
*/
protected static function boot()
{
parent::boot();
static::creating(function ($role) {
if (empty($role->ulid)) {
$role->ulid = Str::ulid()->toBase32(); // Generate ULID automatically
}
});
}
/**
* Assign this role to a user.
*/
public function assignToUser(User $user): void
{
DB::table('model_has_roles')->insert([
'role_id' => $this->id,
'model_id' => $user->id,
'model_type' => User::class,
'model_ulid' => $user->ulid, // Add user ULID during insertion
'created_at' => now(),
'updated_at' => now(),
]);
}
/**
* The users that belong to the role.
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class, 'model_has_roles', 'role_id', 'model_id')
->withPivot('model_ulid', 'model_type'); // Include ULID and model type in pivot
}
}
Permission.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Spatie\Permission\Models\Permission as SpatiePermission;
use App\Models\Permission as CustomPermission;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use App\Models\Role;
class Permission extends SpatiePermission
{
// Adding HasFactory trait for the Permission model
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'guard_name',
];
/**
* The attributes that should be cast to native types.
*
* @var array<string, string>
*/
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* The roles that belong to the permission.
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
}
Permission.php
<?php
return [
'models' => [
'permission' => App\Models\Permission::class,
'role' => Spatie\Permission\Models\Role::class,
],
'table_names' => [
'roles' => 'roles',
'permissions' => 'permissions',
'model_has_permissions' => 'model_has_permissions',
'model_has_roles' => 'model_has_roles',
'role_has_permissions' => 'role_has_permissions',
],
'column_names' => [
'role_pivot_key' => null, //default 'role_id',
'permission_pivot_key' => null, //default 'permission_id',
'model_morph_key' => 'model_id',
'team_foreign_key' => 'team_id',
],
'register_permission_check_method' => true,
'register_octane_reset_listener' => false,
'teams' => false,
'use_passport_client_credentials' => false,
'display_permission_in_exception' => false,
'display_role_in_exception' => false,
'enable_wildcard_permission' => false,
'cache' => [
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
'key' => 'spatie.permission.cache',
'store' => 'default',
],
];
AuthController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\UserProvider;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Events\Verified;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
use Illuminate\Validation\ValidationException;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\View\View;
use App\Models\Store; // Import the Store model
use Illuminate\Support\Facades\Cache;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\DB;
class AuthController extends Controller
{
/**
* Register a new user.
* When registering a new user, if the role is Admin or User, you might need to assign them to a store.
* You could add a store_id to the request, and assign that store_id to the new use
*/
public function register(Request $request): JsonResponse
{
\Log::info('AuthController > Start of register method');
// Validate request data with required fields
$validatedData = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
'role' => ['required', 'in:SuperAdmin,Admin,User'],
'store_code' => ['nullable', 'exists:stores,store_code'],
'phone' => ['nullable', 'string', 'max:15'],
'avatar_url' => ['nullable', 'string', 'max:255'],
'description' => ['nullable', 'string'],
'wpuserId' => ['nullable', 'string'],
]);
\Log::info('AuthController > Request data validated', ['validatedData' => $validatedData]);
// Determine the role of the user
$roleName = $validatedData['role'];
// Check if the role exists
$roleExists = Role::where('name', $roleName)->exists();
\Log::info('AuthController > Role existence check', ['role' => $roleName, 'exists' => $roleExists]);
if (!$roleExists) {
\Log::error('AuthController > Role does not exist', ['role' => $roleName]);
return response()->json(['error' => 'The specified role does not exist.'], 422);
}
// Validate store_code based on role
if (in_array($roleName, ['Admin', 'User']) && empty($validatedData['store_code'])) {
\Log::error('AuthController > Store code is missing for role', ['role' => $roleName]);
return response()->json(['error' => 'store_code is required for Admin and User roles.'], 422);
}
// Get store_id based on store_code if provided
$store_id = null;
if (!empty($validatedData['store_code'])) {
$store = Store::where('store_code', $validatedData['store_code'])->first();
if (!$store) {
\Log::error('AuthController > Invalid store code provided', ['store_code' => $validatedData['store_code']]);
return response()->json(['error' => 'Invalid store code provided.'], 422);
}
$store_id = $store->id;
\Log::info('AuthController > Store resolved', ['store_code' => $validatedData['store_code'], 'store_id' => $store_id]);
}
// Wrap user creation and role assignment in a database transaction
try {
DB::beginTransaction();
\Log::info('AuthController > Beginning user registration transaction');
// Create the user
$user = User::create([
'name' => $validatedData['name'],
'email' => $validatedData['email'],
'password' => Hash::make($validatedData['password']),
'store_id' => $store_id,
'phone' => $validatedData['phone'] ?? null,
'avatar_url' => $validatedData['avatar_url'] ?? null,
'description' => $validatedData['description'] ?? null,
'wpuserId' => $validatedData['wpuserId'] ?? null,
]);
\Log::info('AuthController > User created successfully', ['user_id' => $user->id]);
// Log the traits used by the User model
\Log::info('AuthController > User traits', ['traits' => class_uses($user)]);
// Assign the role to the user
$user->assignRole($roleName);
\Log::info('AuthController > Role assigned to user', ['user_id' => $user->id, 'role' => $roleName]);
// Commit the transaction
DB::commit();
\Log::info('AuthController > Transaction committed successfully');
// Fire the Registered event
event(new Registered($user));
\Log::info('AuthController > Registered event fired', ['user_id' => $user->id]);
return response()->json(['message' => 'User registered successfully'], 201);
} catch (\Exception $e) {
// Rollback the transaction on failure
DB::rollBack();
\Log::error('AuthController > Failed to register user', ['error' => $e->getMessage()]);
// Return an error response
return response()->json([
'error' => 'AuthController > Failed to register user: ' . $e->getMessage(),
], 500);
}
}
the Log file:
[2024-11-18 01:43:06] local.INFO: AuthController > Start of register method [2024-11-18 01:43:06] local.INFO: AuthController > Request data validated {"validatedData":{"name":"John Doe","email":"[email protected]","password":"password123","role":"Admin","store_code":"3456789","phone":"+1234567890","avatar_url":"https://example.com/avatar.jpg","description":"This is a description.","serId":"wp_12345"}} [2024-11-18 01:43:06] local.INFO: AuthController > Role existence check {"role":"Admin","exists":true} [2024-11-18 01:43:06] local.INFO: AuthController > Store resolved {"store_code":"3456789","store_id":1} [2024-11-18 01:43:06] local.INFO: AuthController > Beginning user registration transaction [2024-11-18 01:43:07] local.INFO: AuthController > User created successfully {"user_id":13} [2024-11-18 01:43:07] local.INFO: AuthController > User traits {"traits":{"Laravel\Sanctum\HasApiTokens":"Laravel\Sanctum\HasApiTokens","Illuminate\Database\Eloquent\Factories\HasFactory":"Illuminate\Database\Eloquent\Factories\HasFactory","Illuminate\Notifications\Notifiable":"Illuminate\Notifications\Notifiable","Spatie\Permission\Traits\HasRoles":"Spatie\Permission\Traits\HasRoles"}} [2024-11-18 01:43:07] local.INFO: User.php > Inside assignRole method {"roles":["Admin"],"user_id":13} [2024-11-18 01:43:07] local.INFO: User.php > Assigning roles to user {"user_ulid":"01JCYF054BBP8EQCT4ZN2J","user_id":13,"roles":["Admin"]} [2024-11-18 01:43:07] local.INFO: User.php > Inside try roles ["Admin"] [2024-11-18 01:43:07] local.ERROR: User.php > Error assigning roles {"error_message":"Call to undefined method App\Models\User::assignRole()","user_id":13} [2024-11-18 01:43:07] local.ERROR: AuthController > Failed to register user {"error":"Call to undefined method App\Models\User::assignRole()"}
Upvotes: 0
Views: 91
Reputation: 9
First, modify the User model to properly implement Illuminate\Database\Eloquent\Concerns\HasUlids
Now in your user model
public function uniqueIds(): array
{
return ['ulid'];
}
make sure in your migration $table->ulid('id');
then
php artisan optimize:clear
Upvotes: -1