Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel View Models
https://github.com/spatie/laravel-view-models
Admin
Laravel View Models is a package that provides a simple way to encapsulate complex view logic in
...
Tokens:
3,439
Snippets:
41
Trust Score:
8.5
Update:
1 day ago
Context
Skills
Chat
Benchmark
87.7
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel View Models Laravel View Models is a package by Spatie that provides a clean way to encapsulate complex view logic into dedicated classes. Instead of cluttering controllers with data preparation code, view models take raw data and transform it into something directly usable by views, keeping controllers thin and focused on their primary responsibility. The package offers automatic exposure of public methods and properties to views, support for returning JSON responses for AJAX requests, callable methods with parameters for use in Blade templates, and an artisan command for generating new view models. View models implement both `Arrayable` and `Responsable` interfaces, making them seamlessly integrate with Laravel's view system and response handling. ## Installation Install the package via Composer to add view model support to your Laravel application. ```bash composer require spatie/laravel-view-models ``` ## Creating a ViewModel Class Extend the base `ViewModel` class to create a view model that automatically exposes all public methods and properties to your views. Methods without parameters are executed and their return values are exposed, while methods with parameters become callable closures in the view. ```php <?php namespace App\ViewModels; use App\Models\User; use App\Models\Post; use App\Models\Category; use Illuminate\Support\Collection; use Spatie\ViewModels\ViewModel; class PostViewModel extends ViewModel { // Public properties are automatically exposed to the view public $user; public $post; public $indexUrl = null; public function __construct(User $user, Post $post = null) { $this->user = $user; $this->post = $post; $this->indexUrl = action([PostsController::class, 'index']); } // Methods without parameters: return value is exposed as $post public function post(): Post { return $this->post ?? new Post(); } // Return value is exposed as $categories public function categories(): Collection { return Category::canBeUsedBy($this->user)->get(); } } ``` ## Using ViewModel in Controllers Pass the view model directly to the `view()` helper. Laravel automatically converts it to an array, making all public methods and properties available in the view. ```php <?php namespace App\Http\Controllers; use App\Models\Post; use App\ViewModels\PostViewModel; class PostsController { public function create() { // Create view model with just the user $viewModel = new PostViewModel(current_user()); // Pass directly to view - all public methods/properties become available return view('blog.form', $viewModel); } public function edit(Post $post) { // Create view model with user and existing post $viewModel = new PostViewModel(current_user(), $post); return view('blog.form', $viewModel); } } ``` ## Accessing Data in Blade Views In your Blade templates, access view model data using the method/property names as variables. Public properties and method return values are directly available. ```blade {{-- Access data from public methods --}} <input type="text" name="title" value="{{ $post->title }}" /> <input type="text" name="body" value="{{ $post->body }}" /> {{-- Loop through collection returned by categories() method --}} <select name="category_id"> @foreach ($categories as $category) <option value="{{ $category->id }}">{{ $category->name }}</option> @endforeach </select> {{-- Access public property directly --}} <a href="{{ $indexUrl }}">Back to Posts</a> ``` ## Ignoring Methods Use the `$ignore` property to prevent specific public methods from being exposed to the view. Magic methods starting with `__` are automatically ignored. ```php <?php namespace App\ViewModels; use Spatie\ViewModels\ViewModel; class PostViewModel extends ViewModel { // List methods to exclude from view exposure protected $ignore = ['ignoredMethod', 'helperMethod']; public function post() { return $this->helperMethod(); } // This method will NOT be available in the view public function ignoredMethod() { return 'This is hidden from the view'; } // This helper is also hidden from the view public function helperMethod() { return Post::with('author')->find($this->postId); } } ``` ## Returning ViewModel as Response Return a view model directly from a controller to get a JSON response by default. This is useful for AJAX requests and API endpoints. ```php <?php namespace App\Http\Controllers; use App\Models\Post; use App\ViewModels\PostViewModel; use Illuminate\Http\Request; class PostsController { // Returns JSON response by default public function update(Request $request, Post $post) { $post->update($request->validated()); // Returning ViewModel directly outputs JSON return new PostViewModel($post); } // Response when Accept: application/json header is present: // { // "post": {"id": 1, "title": "Updated Title", "body": "Content..."}, // "categories": [{"id": 1, "name": "Tech"}, {"id": 2, "name": "News"}], // "indexUrl": "/posts" // } } ``` ## Returning View with ViewModel Use the `view()` method to specify which Blade template to render. The response type automatically switches between HTML and JSON based on the request's `Accept` header. ```php <?php namespace App\Http\Controllers; use App\Models\Post; use App\ViewModels\PostViewModel; use Illuminate\Http\Request; class PostsController { public function show(Post $post) { // Chain view() to specify the template return (new PostViewModel($post))->view('posts.show'); } public function edit(Post $post) { // Pass additional data to the view return (new PostViewModel(current_user(), $post)) ->view('posts.form', ['editing' => true]); // If request has Accept: application/json header, // JSON is returned instead of the rendered view } } ``` ## Exposing Callable Methods Methods with parameters are exposed as callable closures in the view, allowing you to pass arguments directly in your Blade templates. ```php <?php namespace App\ViewModels; use Carbon\Carbon; use Spatie\ViewModels\ViewModel; class PostViewModel extends ViewModel { public $post; public function __construct(Post $post) { $this->post = $post; } // Method with parameter becomes a callable in the view public function formatDate(Carbon $date): string { return $date->format('F j, Y'); } // Another callable method example public function truncate(string $text, int $length = 100): string { return strlen($text) > $length ? substr($text, 0, $length) . '...' : $text; } } ``` ```blade {{-- Call methods with parameters directly in Blade --}} <p>Published: {{ $formatDate($post->created_at) }}</p> <p>Updated: {{ $formatDate($post->updated_at) }}</p> {{-- Use the truncate callable --}} <p class="excerpt">{{ $truncate($post->body, 150) }}</p> ``` ## Snake Case Property Names Enable snake_case conversion for property and method names by setting `$snakeCase = true`. This converts camelCase names to snake_case when exposing them to views. ```php <?php namespace App\ViewModels; use Spatie\ViewModels\ViewModel; class ArticleViewModel extends ViewModel { // Enable snake_case conversion protected $snakeCase = true; public $publishedDate = '2024-01-15'; public function featuredArticle() { return Article::featured()->first(); } public function relatedPosts() { return Post::related($this->article)->get(); } } // In Blade template, use snake_case names: // $published_date (instead of $publishedDate) // $featured_article (instead of $featuredArticle) // $related_posts (instead of $relatedPosts) ``` ## Artisan Make Command Use the built-in artisan command to generate new view model classes with the correct namespace and directory structure. ```bash # Create a view model in app/ViewModels/HomepageViewModel.php php artisan make:view-model HomepageViewModel # Create in a custom subdirectory: app/Blog/ViewModels/PostsViewModel.php php artisan make:view-model "Blog/PostsViewModel" # Force overwrite if file exists php artisan make:view-model HomepageViewModel --force ``` Generated file structure: ```php <?php namespace App\ViewModels; use Spatie\ViewModels\ViewModel; class HomepageViewModel extends ViewModel { public function __construct() { // } } ``` ## Converting ViewModel to Array Call `toArray()` to get all exposed data as an associative array. This is useful for testing, debugging, or when you need the raw data. ```php <?php use App\ViewModels\PostViewModel; use App\Models\User; use App\Models\Post; $user = User::find(1); $post = Post::find(1); $viewModel = new PostViewModel($user, $post); // Get all exposed data as array $data = $viewModel->toArray(); // Result: // [ // 'user' => User {...}, // 'post' => Post {...}, // 'indexUrl' => '/posts', // 'categories' => Collection {...}, // 'formatDate' => Closure {...} // Callable methods become closures // ] // Test that expected keys exist assert(array_key_exists('post', $data)); assert(array_key_exists('categories', $data)); assert(is_callable($data['formatDate'])); ``` ## Summary Laravel View Models provides an elegant solution for organizing complex view logic in Laravel applications. The primary use case is separating data preparation from controller logic, making controllers thinner and more maintainable. View models excel at handling forms with multiple data sources (like a post edit form needing categories, tags, and user permissions), dashboard views with computed statistics, and any view requiring data transformation before display. Integration with Laravel is seamless through the `Arrayable` and `Responsable` interfaces. View models can be passed directly to views, returned as JSON responses for AJAX, or converted to arrays for testing. The automatic exposure of public methods and properties, combined with callable method support and the snake_case option, provides flexibility for various coding styles. The artisan generator command ensures consistent file structure across the project, making view models a natural extension of Laravel's conventions.