Migrations
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->enum('role', ['admin', 'manager', 'agent', 'customer'])->default('customer');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
<?php
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class)->constrained()->cascadeOnDelete();
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('avatar_original')->nullable();
$table->string('avatar_256x256')->nullable();
$table->string('avatar_512x512')->nullable();
$table->string('avatar_1024x1024')->nullable();
$table->mediumText('short_text')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('admins');
}
};
<?php
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('managers', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class)->constrained()->cascadeOnDelete();
$table->boolean('active')->nullable();
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('address')->nullable();
$table->string('postcode')->nullable();
$table->string('city')->nullable();
$table->string('country')->nullable();
$table->string('phone')->nullable();
$table->string('company_name')->nullable();
$table->string('contact_email')->nullable();
$table->string('website')->nullable();
$table->string('chamber_of_commerce')->nullable();
$table->string('social_security_number')->nullable();
$table->string('vat_number')->nullable();
$table->string('bank_account_name')->nullable();
$table->string('bank_iban')->nullable();
$table->string('bank_bic')->nullable();
$table->float('tokens')->nullable();
$table->integer('call_tariff')->nullable();
$table->integer('chat_tariff')->nullable();
$table->integer('whatsapp_tariff')->nullable();
$table->string('tax_tariff')->nullable();
$table->string('avatar_original')->nullable();
$table->string('avatar_256x256')->nullable();
$table->string('avatar_512x512')->nullable();
$table->string('avatar_1024x1024')->nullable();
$table->mediumText('short_text')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('managers');
}
};
<?php
use App\Models\Manager;
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('agents', function (Blueprint $table) {
$table->id();
$table->unique(['user_id', 'manager_id']);
$table->foreignIdFor(User::class)->constrained()->cascadeOnDelete();
$table->foreignIdFor(Manager::class)->constrained()->cascadeOnDelete();
$table->boolean('active')->nullable();
$table->string('status')->nullable();
$table->dateTime('last_status_change')->nullable();
$table->string('whatsapp_status')->nullable();
$table->dateTime('whatsapp_last_status_change')->nullable();
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('address')->nullable();
$table->string('postcode')->nullable();
$table->string('city')->nullable();
$table->string('country')->nullable();
$table->string('phone')->nullable();
$table->string('company_name')->nullable();
$table->string('contact_email')->nullable();
$table->string('chamber_of_commerce')->nullable();
$table->string('social_security_number')->nullable();
$table->string('vat_number')->nullable();
$table->string('bank_account_name')->nullable();
$table->string('bank_iban')->nullable();
$table->string('bank_bic')->nullable();
$table->integer('reference')->nullable();
$table->boolean('enable_chat')->nullable();
$table->boolean('enable_calling')->nullable();
$table->boolean('enable_whatsapp')->nullable();
$table->string('outbound_number')->nullable();
$table->string('outbound_preferred_number_1')->nullable();
$table->string('outbound_preferred_number_2')->nullable();
$table->string('whatsapp_number')->nullable();
$table->boolean('call_recording')->nullable();
$table->integer('call_tariff')->nullable();
$table->integer('chat_tariff')->nullable();
$table->integer('whatsapp_tariff')->nullable();
$table->integer('call_outpayment')->nullable();
$table->integer('chat_outpayment')->nullable();
$table->integer('whatsapp_outpayment')->nullable();
$table->string('tax_tariff')->nullable();
$table->string('nickname')->nullable();
$table->string('avatar_original')->nullable();
$table->string('avatar_256x256')->nullable();
$table->string('avatar_512x512')->nullable();
$table->string('avatar_1024x1024')->nullable();
$table->mediumText('short_text')->nullable();
$table->longText('long_text')->nullable();
$table->text('meta_description')->nullable();
$table->text('asterisk_username')->nullable();
$table->text('asterisk_password')->nullable();
$table->text('asterisk_realm')->nullable();
$table->string('asterisk_network')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('agents');
}
};
<?php
use App\Models\Manager;
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->unique(['user_id', 'manager_id']);
$table->foreignIdFor(User::class)->constrained()->cascadeOnDelete();
$table->foreignIdFor(Manager::class)->constrained()->cascadeOnDelete();
$table->boolean('active')->nullable();
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('nickname')->nullable();
$table->string('tokens')->nullable();
$table->string('pin')->nullable();
$table->string('avatar_original')->nullable();
$table->string('avatar_256x256')->nullable();
$table->string('avatar_512x512')->nullable();
$table->string('avatar_1024x1024')->nullable();
$table->mediumText('short_text')->nullable();
$table->string('whatsapp_number');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('customers');
}
};
Models
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'email',
'password',
'role'
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function admins(): HasMany
{
return $this->hasMany(Admin::class);
}
public function managers(): HasMany
{
return $this->hasMany(Manager::class);
}
public function agents(): HasMany
{
return $this->hasMany(Agent::class);
}
public function customers(): HasMany
{
return $this->hasMany(Customer::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\URL;
class Admin extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'admins';
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'first_name',
'last_name',
'avatar_original',
'avatar_256x256',
'avatar_512x512',
'avatar_1024x1024',
'short_text'
];
/**
* Get the user that owns this admin profile.
*
* @return BelongsTo<User>
*/
public function User(): BelongsTo
{
return $this->belongsTo(User::class);
}
public static function defaultValueFiller(User $user)
{
switch ($user->role) {
case 'admin':
return [
'first_name' => '',
'last_name' => '',
'avatar_original' => 'avatars/avatar_default_original.jpg',
'avatar_256x256' => 'avatars/avatar_default_256x256.jpg',
'avatar_512x512' => 'avatars/avatar_default_512x512.jpg',
'avatar_1024x1024' => 'avatars/avatar_default_1024x1024.jpg',
'short_text' => 'Handle me like a Laravel model—fillable, relatable, and always ready for a smooth relationship. 😉🔥'
];
break;
case 'manager':
return [
'active' => 0,
'first_name' => '',
'last_name' => '',
'address' => '',
'postcode' => '',
'city' => '',
'country' => '',
'phone' => '',
'company_name' => '',
'contact_email' => '',
'website' => '',
'chamber_of_commerce' => '',
'social_security_number' => '',
'vat_number' => '',
'bank_account_name' => '',
'bank_iban' => '',
'bank_bic' => '',
'tokens' => 0,
'call_tariff' => 25,
'chat_tariff' => 25,
'whatsapp_tariff' => 25,
'tax_tariff' => 21,
'avatar_original' => 'avatars/avatar_default_original.jpg',
'avatar_256x256' => 'avatars/avatar_default_256x256.jpg',
'avatar_512x512' => 'avatars/avatar_default_512x512.jpg',
'avatar_1024x1024' => 'avatars/avatar_default_1024x1024.jpg',
'short_text' => 'Handle me like a Laravel model—fillable, relatable, and always ready for a smooth relationship. 😉🔥'
];
break;
case 'agent':
return [
'active' => 0,
'status' => 'offline',
'last_status_change' => now()->format('Y-m-d H:i:s'),
'whatsapp_status' => 'offline',
'whatsapp_last_status_change' => now()->format('Y-m-d H:i:s'),
'first_name' => '',
'last_name' => '',
'address' => '',
'postcode' => '',
'city' => '',
'country' => '',
'phone' => '',
'company_name' => '',
'contact_email' => '',
'chamber_of_commerce' => '',
'social_security_number' => '',
'vat_number' => '',
'bank_account_name' => '',
'bank_iban' => '',
'bank_bic' => '',
'reference' => 0,
'enable_calling' => 0,
'enable_chat' => 0,
'enable_whatsapp' => 0,
'outbound_number' => 0,
'outbound_preferred_number_1' => 0,
'outbound_preferred_number_2' => 0,
'call_recording' => 0,
'whatsapp_number' => 0,
'call_tariff' => 100,
'chat_tariff' => 100,
'whatsapp_tariff' => 100,
'call_outpayment' => 50,
'chat_outpayment' => 50,
'whatsapp_outpayment' => 50,
'tax_tariff' => 21,
'nickname' => '',
'avatar_original' => 'avatars/avatar_default_original.jpg',
'avatar_256x256' => 'avatars/avatar_default_256x256.jpg',
'avatar_512x512' => 'avatars/avatar_default_512x512.jpg',
'avatar_1024x1024' => 'avatars/avatar_default_1024x1024.jpg',
'short_text' => 'Handle me like a Laravel model—fillable, relatable, and always ready for a smooth relationship. 😉🔥',
'long_text' => 'Handle me like a Laravel model—fillable, relatable, and always ready for a smooth relationship. 😉🔥',
'meta_description' => 'Handle me like a Laravel model—fillable, relatable, and always ready for a smooth relationship. 😉🔥',
'asterisk_username' => 'endpoint' . $user->id,
'asterisk_password' => fake()->numberBetween(100000, 999999),
'asterisk_realm' => parse_url(URL::to('/'), PHP_URL_HOST),
'asterisk_network' => 'pstn'
];
break;
}
}
/**
* Get the allowed profile columns based on the given role.
*
* @param string $role The role of the user (e.g., 'admin', 'manager', 'agent', 'customer').
* @return array|false The list of allowed profile column names for the specified role,
* or false if the role is not recognized.
*/
public static function allowedProfileColumns(string $role): array|false
{
switch ($role) {
case 'admin':
return [
'first_name',
'last_name',
'short_text'
];
break;
case 'manager':
return [
'active',
'first_name',
'last_name',
'address',
'postcode',
'city',
'country',
'phone',
'company_name',
'contact_email',
'website',
'chamber_of_commerce',
'social_security_number',
'vat_number',
'bank_account_name',
'bank_iban',
'bank_bic',
'tokens',
'call_tariff',
'chat_tariff',
'whatsapp_tariff',
'tax_tariff',
'short_text'
];
break;
case 'agent':
return [
'active',
'status',
'whatsapp_status',
'first_name',
'last_name',
'address',
'postcode',
'city',
'country',
'phone',
'company_name',
'contact_email',
'chamber_of_commerce',
'social_security_number',
'vat_number',
'bank_account_name',
'bank_iban',
'bank_bic',
'reference',
'enable_calling',
'enable_chat',
'enable_whatsapp',
'outbound_number',
'outbound_preferred_number_1',
'outbound_preferred_number_2',
'call_recording',
'whatsapp_number',
'call_tariff',
'chat_tariff',
'whatsapp_tariff',
'call_outpayment',
'chat_outpayment',
'whatsapp_outpayment',
'tax_tariff',
'nickname',
'short_text',
'long_text',
'meta_description',
'asterisk_username',
'asterisk_password',
'asterisk_realm',
'asterisk_network',
];
case 'customer':
return [
'active',
'first_name',
'last_name',
'nickname',
'tokens',
'whatsapp_number',
'short_text'
];
break;
}
return false;
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Manager extends Model
{
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'managers';
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'active',
'first_name',
'last_name',
'address',
'postcode',
'city',
'country',
'phone',
'company_name',
'contact_email',
'website',
'chamber_of_commerce',
'social_security_number',
'vat_number',
'bank_account_name',
'bank_iban',
'bank_bic',
'tokens',
'call_tariff',
'chat_tariff',
'whatsapp_tariff',
'tax_tariff',
'avatar_original',
'avatar_256x256',
'avatar_512x512',
'avatar_1024x1024',
'short_text',
];
/**
* Get the user associated with the manager.
*
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->BelongsTo(User::class);
}
public function agents(){
return $this->hasMany(Agent::class);
}
public function customers(){
return $this->hasMany(Customer::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Agent extends Model
{
protected $table = 'agents';
protected $fillable = [
'manager_id',
'active',
'status',
'last_status_change',
'whatsapp_status',
'whatsapp_last_status_change',
'first_name',
'last_name',
'address',
'postcode',
'city',
'country',
'phone',
'company_name',
'contact_email',
'website',
'chamber_of_commerce',
'social_security_number',
'vat_number',
'bank_account_name',
'bank_iban',
'bank_bic',
'reference',
'enable_chat',
'enable_calling',
'enable_whatsapp',
'outbound_number',
'outbound_preferred_number_1',
'outbound_preferred_number_2',
'call_recording',
'call_number',
'whatsapp_number',
'call_tariff',
'chat_tariff',
'whatsapp_tariff',
'call_outpayment',
'chat_outpayment',
'whatsapp_outpayment',
'tax_tariff',
'nickname',
'avatar_original',
'avatar_256x256',
'avatar_512x512',
'avatar_1024x1024',
'short_text',
'long_text',
'meta_description',
'asterisk_username',
'asterisk_password',
'asterisk_realm',
'asterisk_network',
];
public function user(){
return $this->belongsTo(User::class);
}
public function manager(){
return $this->belongsTo(Manager::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
protected $table = 'customers';
protected $fillable = [
'manager_id',
'active',
'first_name',
'last_name',
'nickname',
'tokens',
'pin',
'avatar_original',
'avatar_256x256',
'avatar_512x512',
'avatar_1024x1024',
'short_text',
'whatsapp_number',
];
public function user(){
return $this->belongsTo(User::class);
}
public function manager(){
return $this->belongsTo(Manager::class);
}
}
public function destroy(User $user): RedirectResponse
{
// Is allowed to do this ?
if (!auth()->check() || !auth()->user()->can('isAdmin', auth()->user())) {
return redirect('/auth/login');
}
// Get the avatar locations
$avatarUrls = User::with([$user->role . 's' => function ($query) {
$query->select('user_id', 'avatar_original', 'avatar_256x256', 'avatar_512x512', 'avatar_1024x1024');
}])
->where('id', $user->id)
->first();
// Avatar location names
$avatarKeys = ['avatar_original', 'avatar_256x256', 'avatar_512x512', 'avatar_1024x1024'];
// Delete the avatars
foreach ($avatarKeys as $avatarKey) {
if (Storage::disk('public')->exists($avatarUrls->{$user->role . 's'}->first()->{$avatarKey})) {
Storage::disk('public')->delete($avatarUrls->{$user->role . 's'}->first()->{$avatarKey});
}
}
// Delete the user
$user->delete();
return redirect('/admin/users');
}
This is working
-
When deleting admin the user table user is removed along with the admin record from the admin table.
-
When deleting agent the user table user is removed along with the admin record from the agent table.
-
When deleting a customer the user table user is removed along with the customer record from the customer table.
What is not working
- When deleting a manager the user table users belonging to the manager are not removed. The records from the manager, agent and customer are remove. The manager itself are also removed.
(topic before was messed up)