PHP code example of mawena / maravel

1. Go to this page and download the library: Download mawena/maravel library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

mawena / maravel example snippets


use App\Http\Controllers\API\AuthController;
use App\Http\Controllers\API\UserController;
use Illuminate\Support\Facades\Route;

Route::controller(AuthController::class)->group(function () {
    Route::post("auth/login", "login");

    Route::middleware('auth:sanctum')->group(function () {
        Route::prefix("/auth")->name("auth.")->group(function () {
            Route::get('data', "data")->name("data");
            Route::delete('logout', "logout")->name("logout");
        });

        // Route pour changer le mot de passe (accessible même si password_change_>name('show');
                Route::put('/{id}', 'update')->name('update');
                Route::delete('/{id}', 'destroy')->name('destroy');
            });

            // Routes supplémentaires sous autorisation
        });
    });
});

// permissions
Schema::create('permissions', function (Blueprint $table) {
    $table->id();
    $table->string('action');
    $table->string('subject');
    $table->string('label')->nullable();
    $table->string('description')->nullable();
    $table->timestamps();
    $table->unique(['action', 'subject']);
});

// roles
Schema::create('roles', function (Blueprint $table) {
    $table->id();
    $table->string('name')->unique();
    $table->string('label')->nullable();
    $table->string('description')->nullable();
    $table->boolean('is_super_admin')->default(false);
    $table->timestamps();
});

// pivots : permission_role (role_id, permission_id) et role_user (role_id, user_id)

Schema::table('users', function (Blueprint $table) {
    $table->boolean('activated')->default(true);
    $table->boolean('password_change_

'defaults' => [
    'pagination' => [
        'per_page' => 8,           // Nombre d'éléments par page
        'max_per_page' => 100,     // Maximum d'éléments par page
    ],
    'validation' => [
        'store' => [],             // Règles de validation par défaut pour store
        'update' => [],            // Règles de validation par défaut pour update
    ],
    'authorization' => [
        'ability_read' => 'read',
        'ability_create' => 'create',
        'ability_update' => 'update',
        'ability_delete' => 'delete',
    ],
],

'filters' => [
    'enabled' => true,
    'types' => [
        'basic' => true,           // Filtres basiques: ?name=value
        'min_max' => true,         // Filtres min/max: ?min<age=18
        'in_not_in' => true,       // Filtres IN/NOT IN: ?in_status=active-pending
        'relations' => true,       // Filtres sur relations
        'search' => true,          // Recherche textuelle: ?search=keyword
        'json' => true,            // Filtres JSON
    ],
],

'models' => [
    'use_model_base' => true,
    'date_format' => 'd/m/Y H:i:s',
    'money_format' => [
        'currency' => 'XOF',
        'decimal_separator' => ',',
        'thousands_separator' => ' ',
    ],
    'auto_casts' => [
        'created_at' => true,
        'updated_at' => true,
    ],
],

'permissions' => [
    'enabled' => true,
    'use_advanced_policies' => true,
    'permission_checks' => [
        'before_all' => true,
        'custom_checks' => true,
    ],
],

'rbac' => [
    'models' => [
        'user' => \App\Models\User::class,
        'role' => \App\Models\Role::class,
        'permission' => \App\Models\Permission::class,
    ],
    'tables' => [
        'roles' => 'roles',
        'permissions' => 'permissions',
        'permission_role' => 'permission_role',
        'role_user' => 'role_user',
    ],
    'super_admin' => [
        'flag_column' => 'is_super_admin',
        'inject_manage_all' => true,
    ],
],

namespace App\Http\Controllers\API;

use Maravel\Http\Controllers\APIController;
use App\Models\Product;

class ProductController extends APIController
{
    protected string $modelClass = Product::class;

    protected array $storeValidationArray = [
        'name' => 'cted array $indexSearchFieldList = ['name', 'description'];
}

use App\Http\Controllers\API\ProductController;

Route::apiResource('products', ProductController::class);

class ProductController extends APIController
{
    // OBLIGATOIRE : Classe du modèle Eloquent
    protected string $modelClass = Product::class;

    // Validation pour la création
    protected array $storeValidationArray = [];

    // Validation pour la mise à jour
    protected array $updateValidationArray = [];

    // Champs de recherche textuelle
    protected array $indexSearchFieldList = [];

    // Relations à charger automatiquement
    protected array $indexWithArray = [];
    protected array $showWithArray = [];

    // Nom de l'abilité pour les permissions
    protected string $readAbilityName = 'read';
    protected string $createAbilityName = 'create';
    protected string $updateAbilityName = 'update';
    protected string $deleteAbilityName = 'delete';

    // Activation/désactivation des permissions
    protected bool $indexCheckAbility = true;
    protected bool $showCheckAbility = true;
    protected bool $storeCheckAbility = true;
    protected bool $updateCheckAbility = true;
    protected bool $deleteCheckAbility = true;
}

// Route : POST /api/upload-chunk
// Paramètres :
// - file: Le morceau de fichier (UploadedFile)
// - index: L'index du morceau (string/int)
// - filename: Le nom du fichier complet

// Exemple d'utilisation côté client (JavaScript) :
const uploadFile = async (file) => {
    const chunkSize = 1024 * 1024; // 1MB par chunk
    const chunks = Math.ceil(file.size / chunkSize);

    for (let i = 0; i < chunks; i++) {
        const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
        const formData = new FormData();
        formData.append('file', chunk);
        formData.append('index', i);
        formData.append('filename', file.name);

        await fetch('/api/upload-chunk', {
            method: 'POST',
            body: formData
        });
    }
};

// Route : POST /api/merge-chunks
// Paramètres :
// - filename: Le nom du fichier à fusionner

// Retourne :
// {
//   "status": "file merged",
//   "file_path": "uploads/1234567890/mon-fichier.pdf"
// }

// Exemple d'utilisation :
Route::post('/upload-chunk', [MyController::class, 'uploadChunk']);
Route::post('/merge-chunks', [MyController::class, 'mergeChunks']);

namespace App\Models;

use Maravel\Models\ModelBase;

class Product extends ModelBase
{
    protected $fillable = ['name', 'price', 'stock', 'description', 'views_count'];

    // Définir les casts personnalisés
    protected array $dateCasts = [
        'published_at' => 'd/m/Y',  // Format personnalisé
    ];

    protected array $moneyCasts = [
        'price',                     // Formaté avec la config par défaut
    ];

    protected array $booleanCasts = [
        'is_active',                 // Formaté en 'Oui'/'Non'
    ];

    protected array $big_integer_casts = [
        'views_count',               // Converti en int + formaté avec espaces
    ];

    protected array $enumCasts = [
        [
            'colum_name' => 'status',
            'choices' => [
                'active' => 'Actif',
                'inactive' => 'Inactif',
                'pending' => 'En attente',
            ],
            'additional_column_name' => 'status_label',
        ],
    ];
}

protected array $enumCasts = [
    [
        'colum_name' => 'status',                    // Nom de la colonne dans la base de données
        'choices' => [                                // Mapping valeur => label
            'active' => 'Actif',
            'inactive' => 'Inactif',
            'pending' => 'En attente',
        ],
        'additional_column_name' => 'status_label',   // Nom de l'attribut formaté dans le JSON
    ],
    [
        'colum_name' => 'priority',
        'choices' => [
            'low' => 'Basse',
            'medium' => 'Moyenne',
            'high' => 'Haute',
        ],
        'additional_column_name' => 'priority_text',
    ],
];

$product = Product::find(1);
// Si $product->status = 'active'

$product->toArray();
// Retourne :
[
    'id' => 1,
    'status' => 'active',              // Valeur brute
    'status_label' => 'Actif',         // Valeur formatée avec le nom personnalisé
    'priority' => 'high',
    'priority_text' => 'Haute',
    // ...
]

$product = Product::find(1);

// Ajouter un cast date
$product->addDateCast('last_order_at', 'd/m/Y H:i');

// Ajouter un cast money
$product->addMoneyCast('cost');

// Ajouter un cast enum
$product->addEnumCast('type', [
    'physical' => 'Produit physique',
    'digital' => 'Produit numérique',
]);

$product = Product::find(1);
// $product->views_count => 1500000 (int)
// $product->views_count_fr => "1 500 000" (string formatée)

$product = Product::find(1);
$product->toArray();
// Retourne :
[
    'is_active' => 1,              // Valeur convertie en int (0 ou 1)
    'is_active_formatted' => true, // Valeur booléenne (true ou false)
]

use Maravel\Models\ModelTrait;

class MyModel extends Model
{
    use ModelTrait;

    protected $dateCasts = ['published_at' => 'd/m/Y'];
    protected $moneyCasts = ['price', 'cost'];
    protected $booleanCasts = ['is_active'];
    protected $big_integer_casts = ['views_count', 'total_sales'];
    protected $floatCasts = ['rating'];
    protected $enumCasts = [
        [
            'colum_name' => 'status',
            'choices' => ['draft' => 'Brouillon', 'published' => 'Publié'],
            'additional_column_name' => 'status_label'
        ]
    ];
}

protected $big_integer_casts = ['views_count', 'total_sales'];

// Résultat dans toArray() :
// 'views_count' => 1500000        // Converti en int
// 'views_count_fr' => '1 500 000' // Version formatée

use Illuminate\Database\Eloquent\Model;
use Maravel\Models\ModelTrait;

class Product extends Model
{
    use ModelTrait; // Utilisation directe du trait

    protected $dateCasts = ['launched_at' => 'd/m/Y'];
    protected $moneyCasts = ['price'];
}

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Maravel\Models\AuthenticatableBase;

class User extends AuthenticatableBase
{
    use HasApiTokens, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
        'activated',
        'password_change_ile`)
    protected $enumCasts = [
        [
            'colum_name' => 'activated',
            'additional_column_name' => 'activated_fr',
            'choices' => [
                1 => 'Oui',
                0 => 'Non',
            ],
        ],
        [
            'colum_name' => 'password_change_ écrire ici.
}

// Route : PUT /api/users/update-password
// Corps de la requête :
{
    "current_password": "ancien_mot_de_passe",
    "new_password": "nouveau_mot_de_passe",
    "new_password_confirmation": "nouveau_mot_de_passe"
}

// Réponse en cas de succès :
{
    "message": "Mot de passe modifié avec succès",
    "user": { ... }
}

->withMiddleware(function (Middleware $middleware) {
    // Middleware Maravel pour vérifier le statut du compte
    $middleware->alias([
        'account.status' => \Maravel\Http\Middleware\AccountStatusMiddleware::class,
    ]);
})

// Sur un groupe de routes
Route::middleware(['auth:sanctum', 'account.status'])->group(function () {
    Route::apiResource('posts', PostController::class);
    Route::apiResource('products', ProductController::class);
});

// Sur une route spécifique
Route::get('/dashboard', [DashboardController::class, 'index'])
    ->middleware(['auth:sanctum', 'account.status']);

$user = User::find(5);
$user->activated = false;
$user->save();
// L'utilisateur ne pourra plus accéder aux routes protégées par account.status

$user = User::find(5);
$user->password_change_ son mot de passe avant d'accéder aux routes protégées

$user = User::find(5);
$user->activated = true;
$user->password_change_

namespace App\Policies;

use Maravel\Policies\BasePolicy;
use App\Models\User;
use App\Models\Product;

class ProductPolicy extends BasePolicy
{
    // Le sujet pour les vérifications de permissions
    protected string $subject = 'product';

    // Méthode before() pour vérifications globales
    public function before(User $user, string $ability): ?bool
    {
        // Les super-admins ont tous les droits
        if ($user->isAdmin()) {
            return true;
        }

        return null; // Continuer les vérifications normales
    }

    // Permission personnalisée
    public function publish(User $user, Product $product): bool
    {
        return $this->checkCustomPermission($user, ['publish'], $this->subject)
            && $product->user_id === $user->id;
    }
}

use App\Models\Product;
use App\Policies\ProductPolicy;

protected $policies = [
    Product::class => ProductPolicy::class,
];

use Maravel\Http\Traits\CustomResponseTrait;

class MyController extends Controller
{
    use CustomResponseTrait;

    public function index()
    {
        $data = ['items' => [...]];
        return $this->responseOk($data, ['Success'], 200);
    }

    public function error()
    {
        // 422 = Unprocessable Entity (erreur de validation)
        return $this->responseError(['field' => ['Error message']], 422);
    }
}

use Maravel\Http\Traits\ControllerHelperTrait;

class MyController extends Controller
{
    use ControllerHelperTrait;

    public function index(Request $request)
    {
        $query = Product::query();

        // Ajouter des filtres
        $query = $this->queryFilter($query, $request->all(), 'Product');

        // Ajouter recherche
        $query = $this->querySearch($query, ['name', 'description'], $request->search);

        // Ajouter relations
        $query = $this->queryRelationAdd($query, $request->all(), 'Product');

        return $query->get();
    }
}

// Dans votre modèle, définissez des méthodes "reducer"
class Product extends ModelBase
{
    /**
     * Reducer pour ajouter des statistiques
     */
    public function statsReducer($collection, $requestData)
    {
        // Ajouter des statistiques calculées
        $collection->map(function ($item) {
            $item->total_revenue = $item->price * $item->sold_count;
            return $item;
        });

        return $collection;
    }

    /**
     * Reducer pour filtrer selon l'utilisateur
     */
    public function userFilterReducer($collection, $requestData)
    {
        $userId = $requestData['user_id'] ?? null;
        if ($userId) {
            return $collection->where('user_id', $userId)->values();
        }
        return $collection;
    }
}

// Utilisation dans la requête API :
// GET /api/products?reduce_stats=true
// GET /api/products?reduce_user_filter=true&user_id=5

// Le système cherchera automatiquement les méthodes suffixées par "Reducer"

use Maravel\Http\Traits\PermissionCheckerTrait;

class MyController extends Controller
{
    use PermissionCheckerTrait;

    public function index(Request $request)
    {
        $user = $request->user();

        if (!$this->canRead('product', $user)) {
            return response()->json(['error' => 'Unauthorized'], 403);
        }

        if ($this->isAdmin($user)) {
            // Logique admin
        }

        return Product::all();
    }
}

// Sur un groupe de routes
Route::middleware(['auth:sanctum', 'maravel.fields'])->group(function () {
    Route::apiResource('products', ProductController::class);
    Route::apiResource('users', UserController::class);
});

// Sur une route spécifique
Route::get('/users/{id}', [UserController::class, 'show'])
    ->middleware('maravel.fields');

$this->responseOk($data, $messages, 201);          // 201 Created
$this->responseError(['email' => ['...']], 422);   // 422 Unprocessable Entity

return [
    'errors' => ['quota' => ['Quota dépassé']],
    'status' => 409, // 409 Conflict
];

   // Ancien : $user->profile = 'admin';
   $user->assignRole('admin');
   

$user->assignRole('admin');                  // par nom
$user->assignRole($role, 5);                 // par modèle, par id
$user->syncRoles('manager', 'comptable');    // remplace tous les rôles
$user->removeRole('manager');

$user->isAdmin();                            // au moins un rôle is_super_admin
$user->hasRole('manager');
$user->hasPermissionTo('validate', 'user');  // tient compte de manage / all
$user->roles;                                // relation
$user->ability_rules;                        // règles CASL calculées

class ProductPolicy extends BasePolicy
{
    protected $modelName = "product"; // les actions read/create/update/delete sont gérées
}

use Maravel\Http\Traits\PermissionCheckerTrait;

public function index(Request $request)
{
    if (!$this->canRead('product', $request->user())) {
        abort(403, 'Unauthorized');
    }

    return Product::all();
}

class ProductController extends APIController
{
    // Validation personnalisée
    protected function storeManualValidationsFunction(array $data): array
    {
        if ($data['price'] > 10000) {
            return ['price' => ['Le prix ne peut pas dépasser 10000']];
        }
        return [];
    }

    // Avant la création
    protected function storeBeforeCreateFunction(array $data): array
    {
        $data['slug'] = Str::slug($data['name']);
        return $data;
    }

    // Après la création
    protected function storeAfterCreateFunction($model): void
    {
        // Envoyer un email
        Mail::to('[email protected]')->send(new ProductCreated($model));
    }

    // Avant le commit en base de données
    protected function storeBeforeCommitFunction($model): void
    {
        // Logique métier
    }

    // Après le commit
    protected function storeAfterCommitFunction($model): void
    {
        // Créer des enregistrements liés
        $model->history()->create(['action' => 'created']);
    }
}

class ProductController extends APIController
{
    // Validation personnalisée
    protected function updateManualValidationsFunction(array $data, $model): array
    {
        if (isset($data['price']) && $data['price'] < $model->cost) {
            return ['price' => ['Le prix ne peut pas être inférieur au coût']];
        }
        return [];
    }

    // Avant la mise à jour
    protected function updateBeforeUpdateFunction(array $data, $model): array
    {
        if (isset($data['name'])) {
            $data['slug'] = Str::slug($data['name']);
        }
        return $data;
    }

    // Après la mise à jour
    protected function updateAfterUpdateFunction($model): void
    {
        Cache::forget("product_{$model->id}");
    }
}

class ProductController extends APIController
{
    // Avant la suppression
    protected function deleteBeforeDeleteFunction($model): void
    {
        // Supprimer les fichiers associés
        Storage::delete($model->images->pluck('path')->toArray());
    }

    // Après la suppression
    protected function deleteAfterDeleteFunction($model): void
    {
        // Logger la suppression
        Log::info("Product {$model->id} deleted");
    }
}

class ProductController extends APIController
{
    protected function indexManualFilter($query, array $requestData)
    {
        // Ajouter des filtres personnalisés complexes
        if (isset($requestData['category_slug'])) {
            $query->whereHas('category', function ($q) use ($requestData) {
                $q->where('slug', $requestData['category_slug']);
            });
        }

        return $query;
    }
}

protected function customAction(
    ?string $authName,
    callable $action,
    mixed $authTarget = null,
    array $requestData = [],
    array $validations = [],
    array $validationsText = [],
    bool $useTransaction = true,
): \Illuminate\Http\JsonResponse

public function update_password(Request $request)
{
    $user = $request->user();

    return $this->customAction(
        authName: "update_password",
        authTarget: $user,
        requestData: $request->all(),
        validations: [
            "old_password" => 'rd)) {
                return [
                    "errors" => ["old_password" => ["Ancien mot de passe incorrect"]],
                    "status" => 400,
                ];
            }

            $user->update(["password" => Hash::make($requestData["new_password"])]);
            $user->tokens()->each(fn ($token) => $token->delete());

            return ["user" => $user];
        },
    );
}

return $this->customAction(
    authName: "export",
    action: fn () => ["url" => $this->generateExportUrl()],
    useTransaction: false,
);

namespace App\Models;

use Maravel\Models\ModelBase;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Post extends ModelBase
{
    protected $fillable = ['title', 'content', 'user_id', 'published_at', 'status'];

    protected array $dateCasts = [
        'published_at' => 'd/m/Y H:i',
    ];

    protected array $enumCasts = [
        [
            'colum_name' => 'status',
            'choices' => [
                'draft' => 'Brouillon',
                'published' => 'Publié',
                'archived' => 'Archivé',
            ],
            'additional_column_name' => 'status_label',
        ],
    ];

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

namespace App\Http\Controllers\API;

use Maravel\Http\Controllers\APIController;
use App\Models\Post;
use Illuminate\Support\Str;

class PostController extends APIController
{
    protected string $modelClass = Post::class;

    protected array $storeValidationArray = [
        'title' => 'ay $indexSearchFieldList = ['title', 'content'];
    protected array $indexWithArray = ['user'];

    protected function storeBeforeCreateFunction(array $data): array
    {
        $data['slug'] = Str::slug($data['title']);
        $data['user_id'] = auth()->id();

        if ($data['status'] === 'published' && !isset($data['published_at'])) {
            $data['published_at'] = now();
        }

        return $data;
    }

    protected function indexManualFilter($query, array $requestData)
    {
        // Seuls les posts publiés pour les non-admins
        if (!auth()->user()?->isAdmin()) {
            $query->where('status', 'published');
        }

        return $query;
    }
}

Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('posts', PostController::class);
});

namespace App\Models;

use Maravel\Models\ModelBase;

class Product extends ModelBase
{
    protected $fillable = ['name', 'description', 'price', 'cost', 'stock', 'is_active'];

    protected array $moneyCasts = ['price', 'cost'];
    protected array $booleanCasts = ['is_active'];
}

namespace App\Policies;

use Maravel\Policies\BasePolicy;
use App\Models\User;
use App\Models\Product;

class ProductPolicy extends BasePolicy
{
    protected string $subject = 'product';

    public function before(User $user, string $ability): ?bool
    {
        if ($user->isAdmin()) {
            return true;
        }

        return null;
    }

    public function viewAny(User $user): bool
    {
        // Tout le monde peut voir les produits
        return true;
    }

    public function create(User $user): bool
    {
        return $this->checkCustomPermission($user, ['create'], $this->subject);
    }

    public function update(User $user, Product $product): bool
    {
        return $this->checkCustomPermission($user, ['update'], $this->subject);
    }

    public function updatePrice(User $user, Product $product): bool
    {
        // Seuls les admins et managers peuvent modifier les prix
        return $user->isAdmin() || $user->hasRole('manager');
    }
}

namespace App\Http\Controllers\API;

use Maravel\Http\Controllers\APIController;
use App\Models\Product;

class ProductController extends APIController
{
    protected string $modelClass = Product::class;

    protected array $storeValidationArray = [
        'name' => '     'name' => 'string|max:255',
        'description' => 'nullable|string',
        'price' => 'numeric|min:0',
        'cost' => 'numeric|min:0',
        'stock' => 'integer|min:0',
        'is_active' => 'boolean',
    ];

    protected array $indexSearchFieldList = ['name', 'description'];

    protected bool $indexCheckAbility = false; // Désactiver pour viewAny

    protected function updateManualValidationsFunction(array $data, $model): array
    {
        // Vérifier la permission pour modifier le prix
        if (isset($data['price'])) {
            if (!auth()->user()->can('updatePrice', $model)) {
                return ['price' => ['Vous n\'avez pas la permission de modifier le prix']];
            }
        }

        // Vérifier que le prix est supérieur au coût
        if (isset($data['price']) && $data['price'] < ($data['cost'] ?? $model->cost)) {
            return ['price' => ['Le prix doit être supérieur au coût']];
        }

        return [];
    }

    protected function storeAfterCommitFunction($model): void
    {
        // Créer l'historique de stock
        $model->stockHistory()->create([
            'quantity' => $model->stock,
            'type' => 'initial',
            'user_id' => auth()->id(),
        ]);
    }
}

// Pour un utilisateur "manager"
$user->ability_rules = [
    [
        'subject' => ['product'],
        'action' => ['read', 'create', 'update'],
    ],
];

// Pour un utilisateur "seller"
$user->ability_rules = [
    [
        'subject' => ['product'],
        'action' => ['read'],
    ],
];
bash
php artisan maravel:install
bash
php artisan db:seed --class=RolePermissionSeeder   # crée le rôle "admin" (is_super_admin) + permissions de base
bash
php artisan vendor:publish --provider="Maravel\Providers\AdvancedApiControllerServiceProvider" --tag="advanced-api-controller-config"
bash
php artisan make:maravel.controller ProductController
json
   {
       "error": "Vous devez changer votre mot de passe avant de continuer."
   }
   
bash
php artisan make:maravel.policy ProductPolicy
bash
php artisan maravel:install
bash
php artisan make:maravel.controller ProductController
bash
# Modèle standard avec ModelBase
php artisan make:maravel.model Product

# Modèle User avec AuthenticatableBase (pour l'authentification)
php artisan make:maravel.model User --authenticatable
bash
php artisan make:maravel.policy ProductPolicy

GET /api/products?name=iPhone&category_id=2

GET /api/products?min<price=100&max<price=500
GET /api/products?min<stock=10

GET /api/products?have_reviews=true        // Produits qui ont au moins un avis
GET /api/products?doesnt_have_reviews=true // Produits sans aucun avis

GET /api/products?have_category>images=true // Produits dont la catégorie a des images

GET /api/products?search=iPhone
bash
   composer update mawena/maravel
   php artisan maravel:install
   
bash
   php artisan migrate
   php artisan db:seed --class=RolePermissionSeeder