Posts

Since 2024
Add Comments to your Laravel Application with the Commenter Package
Add Comments to your Laravel Application with the Commenter Package

The Commenter package for Laravel "is a feature-rich modern package that addresses all your commenting needs in a Laravel application." The package includes an admin panel to manage comments across all "commentable" models. Commenter demo project example The package models include a Commenter, and a Commentable model. You can use this package with your Eloquent models by implementing the CommentableContract and CommenterContract interfaces. The following example illustrates that Post is commentable, and User is a commenter: use LakM\Comments\Concerns\Commentable; use LakM\Comments\Contracts\CommentableContract; class Post extends Model implements CommentableContract { use Commentable; } And the Commentable interface on the User model, for example: namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use LakM\Comments\Concerns\Commenter; class User extends Authenticatable { use HasFactory, Notifiable, Commenter; } Commenter features: Simple, modern, and user-friendly interfaces. Mobile responsiveness. WYSIWYG editor. Syntax highlighting. Robust security features. Effective spam prevention. Reaction options. Support for threaded replies. User mention functionality. Display a list of users who reacted (auth mode only). Pagination. Support is available for both authentication mode and guest mode (mutually exclusive). Advanced filtering and sorting options. Responsive design using a combination of Livewire and Alpine.js. Optimized performance. And much more. To get started with this package, I recommend reading the Commenter documentation. You can view the source code on GitHub, and the package author also has a demo project to get a basic overview of this package. The post Add Comments to your Laravel Application with the Commenter Package appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.

Take the Annual State of Laravel 2024 Survey
Take the Annual State of Laravel 2024 Survey

The annual State of Laravel survey is open for 2024, and you can participate to help identify trends in how the ecosystem has changed over the last twelve months. The State of Laravel survey is "an attempt to gain insight into the representation of the diverse technologies and behaviors of this outstanding community." The survey continues to build on surfacing long-term trends in Laravel and surrounding ecosystems over the last four years. The survey collects statistics about developer demographics, tech stack usage, development tools, production, and opinion categories. Let's highlight a few trends we're seeing from the results of the State of Laravel Survey 2023: Which other programming languages do you use? JavaScript and TypeScript dominate the top "other" programming languages respondents used in projects. JavaScript and TypeScript are ubiquitous languages, so it is no surprise that virtually every respondent uses these languages. Outside of the JS ecosystem, Python remains strong, with 25% of users writing Python. Golang saw a slight uptick of +3% (~12%) in 2023 as well: What are you using to make your frontend reactive? Vue.js remains king and holds steady in the ~ 60% range of respondents using Vue on the front end in some capacity. React is increasing slowly, and I want to see if React sees a nice uptick in adoption in the 2024 survey. Livewire will be another front-end choice, and we've seen a steady increase in tutorials, projects, and developers sharing Livewire-related code. Filament also enters the survey under the new category Administration Panels, which might contribute to an uptick in Livewire usage. Which versions of PHP are you using for your applications? The 2024 survey introduces the adoption of PHP 8.3 to the PHP versions developers use in applications (multiple choice). Laravel has done an excellent job of supporting backward compatibility while using modern PHP 8 features. The latest version, Laravel 11, requires PHP ^8.2, and Laravel 10 requires PHP ^8.1. I'd expect most developers are using PHP 8.3 this year, especially with development options like Laravel Herd: Are you using any library to build administration panels? Administration panels are a new survey question this year and include Laravel Nova, Filament, and Backpack. I don't have any solid predictions in this category, but I would guess that we will see a healthy number of responses for all three options. There are other options in this space; it would be interesting to see how many people would tick the "other" box here as well. Take the Survey Take the State of Laravel 2024 survey to contribute your voice to the community. By sharing and participating, you can help us gain more insights into this amazing community. The post Take the Annual State of Laravel 2024 Survey appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.

Laravel Advanced String Package
Laravel Advanced String Package

Welcome to the first episode of a new "Creator Spotlight" video series where we interview Laravel developers about the things they are creating. Today we talk with Matt Stenson who created a Laravel Advanced String package that adds extra advanced string manipulation methods to the built-in Str class. This package provides extended functionality on strings, such as advanced password generation, data redaction, and more. Available Methods The String methods this package includes are: advPassword Generates a random, secure password. public static function advPassword( $length = 32, $letters = true, $numbers = true, $symbols = true, $spaces = false, $upperLetters = false, $lowerLetters = false, $exclude = [] ) charWrap Wraps a string at a given number of characters regardless of words. public static function charWrap( $string, $length = 80 ) emailDomain Extracts the domain part of an email address, including subdomains. public static function emailDomain( $string ) readTime Calculates the read time of a string. public static function readTime( $string, $wpm = 200 ) redactCreditCard Redacts credit card numbers in a string. public static function redactCreditCard( $string, $redacted = '********', $exclude = [] ) redactSsn Redacts Social Security Numbers (SSN) in a string. public static function redactSsn( $string, $redacted = '********', $dashes = true, $noDashes = true ) splitName Splits a full name into first name, middle name (if present), and last name, removing any prefixes and suffixes. This method can handle both "Firstname Lastname" and "Lastname, Firstname" formats. public static function splitName( $name ) More information For more info check out the Github Repo for installation details, API docs, and more. The post Laravel Advanced String Package appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.

The Best Laravel Tutorials and Resources for Developers
The Best Laravel Tutorials and Resources for Developers

Laravel is one of the most popular PHP frameworks, known for its elegant syntax and powerful features. According to the JetBrains Development Ecosystem Report 2023, over 60% of PHP developers use Laravel, and nowadays, many opt for this framework for their professional development. Whether you're a beginner just starting with web development or an experienced pro looking to boost your skills, this blog post will guide you through the best educational resources available for mastering Laravel. From online courses and books to YouTube channels and community resources, you'll find everything you need to become proficient in Laravel. Online Courses Online courses are a great way to learn Laravel at your own pace. They offer structured learning paths and hands-on projects to help you build real-world applications. Here are some of the best online courses available: Laracasts Often referred to as the "Netflix for developers", Laracasts, managed by well-known Laravel community member Jeffrey Way, offers a vast library of high-quality video tutorials focused primarily on Laravel. It also includes a series on modern PHP development, testing, and other related technologies. With its active community and discussion forums, Laracasts is an essential resource for any Laravel developer. The content ranges from beginner to advanced levels, covering fundamental concepts, real-world applications, and best practices. Here, you can find the most interesting ones depending on your experience: 30 Days to Learn Laravel is an extensive series of video tutorials by Jeffrey Way that covers Laravel from the basics to advanced topics. It includes topics like routing, controllers, Eloquent ORM, testing, and deployment. The hands-on approach helps learners build applications while understanding the underlying concepts of Laravel. For those who are already familiar with Laravel and ready to go deeper, discover the free PhpStorm for Laravel Developers series that is tailored to help Laravel developers maximize their efficiency using PhpStorm, a JetBrains IDE for PHP. This series covers a variety of topics, including PhpStorm basic setup and configuration, code navigation, refactoring, and debugging, specifically within a Laravel project. The series dives into advanced features like live templates, macros, and integration with Laravel tools such as Tinker and Artisan. Each episode is designed to help developers become more productive by leveraging PhpStorm’s powerful features tailored for Laravel development. This video course doesn’t require a Laracasts subscription. Subscription price: USD 15/month or USD 99/year, providing access to all content, including series on modern PHP development, testing, and more. Some courses are free. Laravel Daily Laravel Daily offers a variety of tutorials and tips focused on Laravel development and is managed by experienced web developer Povilas Korop. The content is updated regularly and covers both beginner and advanced topics in Laravel. If you’ve just started with Laravel, the Laravel 11 For Beginners: Your First Project free course might be an ideal choice for you. It offers a practical introduction to Laravel 11 and, instead of delving deep into theory, focuses on creating a small project to manage a blog. By completing this project, learners will understand the essential features of Laravel, such as routing, Blade templates, database migrations, and Eloquent ORM. For an intermediate level of experience, discover the Laravel Collections Chains: 15 Real Examples course, which focuses on mastering Laravel Collections through 15 practical, practical examples. Laravel Collections provide a fluent, convenient wrapper for working with arrays and data sets in Laravel. This course helps developers leverage the power of Collections to write more efficient and readable code, including various use cases such as data filtering, transformation, aggregation, and complex manipulations. Subscription price: USD 29/month or USD 129/year. Some courses are free. Codecourse Codecourse offers a variety of tutorials on web development at both the beginner and intermediate levels, with a strong focus on Laravel. Check out the Laravel Basics series, which takes you through the most-used parts of the framework to get you up and running in no time. Udemy Udemy is a large educational platform offering a variety of courses across different fields. One notable course for Laravel beginners is PHP with Laravel for Beginners - Become a Master in Laravel. This tutorial is designed to take a learner from a complete beginner to a Laravel master. It covers the basics of PHP and Laravel, including routing, controllers, views, and database interactions with Eloquent ORM. The course includes hands-on projects, allowing you to build real-world applications and solidify your understanding. With lifetime access to the course materials, you can learn at your own pace. Course price: USD 23 (prices may vary). Coursera Coursera is an online learning platform that partners with top universities and organizations worldwide to offer online courses, specializations, degrees, and professional certificates across a wide range of subjects. The Mastering Laravel Framework and PHP course covers the essentials of PHP and the Laravel framework, which is ideal for beginners with some prior PHP knowledge. It starts with setting up a development environment, followed by fundamental PHP concepts, including arrays, functions, and form handling. The course then delves into the Laravel framework, covering key features such as routing, middleware, controllers, and views. Students will learn to build web applications from scratch, applying best practices in Laravel. The course includes video lectures, readings, and quizzes. Course price: free to audit; сertificate available for a fee (typically around USD 49). Books Laravel: Up & Running by Matt Stauffer Approved by the creator of Laravel, Taylor Otwell, Laravel: Up & Running is a comprehensive guide that takes you through the essentials of Laravel, from installation to advanced features. Matt Stauffer, a renowned Laravel developer, explains concepts clearly and provides practical examples. The book covers routing, middleware, controllers, Eloquent ORM, Blade templating, and more. It also dives into advanced topics like testing, queues, and real-time event broadcasting. “Not only is Matt one of the most knowledgeable members of the Laravel community, he is also a fantastic teacher. I’m proud to recommend this book as a thorough, extensive guide to the Laravel framework.” – Taylor Otwell, Creator of Laravel This book is a must-have for both new and experienced Laravel developers looking to solidify and deepen their knowledge. Price: USD 39 (may vary). YouTube Channels YouTube is a fantastic resource for learning Laravel through video tutorials. Here are some of the best channels to help you get started: Traversy Media Traversy Media, created by Brad Traversy, is renowned for its high-quality web development tutorials. On this channel, you can find the Laravel Crash Course tutorial designed for beginners, which covers everything from setting up a Laravel project to deploying it. The Net Ninja The Net Ninja is another well-known source that provides detailed and well-structured tutorials on web development. The Laravel Tutorial for Beginners playlist covers the basics of Laravel, from installation to building a full-fledged web application. Each video is short and focused, making it easy to follow along and grasp the concepts. Documentation and Community Resources Official Laravel documentation The Laravel documentation is a comprehensive and up-to-date resource covering every aspect of the framework. It includes detailed explanations, code examples, and guides on various features such as routing, middleware, authentication, and more. It's an essential resource for both new and experienced developers working with Laravel. Regular updates ensure that you have the latest information on new features and best practices. Laravel News Laravel News is a community-driven portal that provides the latest news, tutorials, and packages for Laravel. It features articles on new releases, best practices, and tips for improving your Laravel projects. The site also includes interviews with prominent Laravel developers and information about upcoming events and conferences. Laravel News is an excellent way to stay updated on the latest trends and developments in the Laravel ecosystem. It's a valuable resource for continuous learning and keeping your skills sharp. Laravel Forum The Laracasts forum is a vibrant community where you can ask questions, share knowledge, and discuss all things Laravel. The forums are frequented by experienced developers and Laravel experts who provide helpful advice and solutions. You can find discussions on a wide range of topics, from troubleshooting specific issues to best practices for Laravel development. Participating in the forums is a great way to connect with other developers and learn from their experiences. It's an excellent resource for getting support and staying engaged with the Laravel community. Laravel Community Laravel.io is a Laravel community portal that provides various resources to help developers learn, share, and connect with others. It includes a forum, a compendium of articles written by community members covering a wide range of topics, a real-time chat that allows developers to communicate with each other for quick assistance, information, and more. Closing thoughts This guide provides a comprehensive overview of the best resources available for learning Laravel. Whether you prefer structured online courses, in-depth books, engaging YouTube tutorials, or active community participation, there's something here for every learner. Dive in, start exploring, and take your Laravel skills to the next level! The post The Best Laravel Tutorials and Resources for Developers appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.

Visual EXPLAIN for MySQL and Laravel
Visual EXPLAIN for MySQL and Laravel

The MySQL Visual Explain tool by Tobias Petry helps users analyze slow queries by providing an easy-to-understand visual representation of MySQL’s EXPLAIN output. This tool enables you to decipher MySQL’s default, often cryptic EXPLAIN output, making it accessible even for those without deep database expertise, providing an excellent visualization of query performance: MySQL Explain example visualization The MySQL Visual Explain website has an accompanying API and an integration for laravel. The Laravel package adds methods to the query builder with various options such as automatically running a visual explain and providing a link, dumping the visual explain, or outputting the URL and stopping execution: // $url will be e.g. https://mysqlexplain.com/explain/01j2gcrbsjet9r8rav114vgfsy $url = Film::where('description', 'like', '%astronaut%') ->visualExplain(); // URL to EXPLAIN will be printed to screen $users = Film::where('description', 'like', '%astronaut%') ->dumpVisualExplain() ->get(); // URL to EXPLAIN will be printed to screen & execution is stopped Film::where('description', 'like', '%astronaut%') ->ddVisualExplain(); // Submit raw queries use Tpetry\MysqlExplain\Facades\MysqlExplain; $url = MysqlExplain::submitQuery( DB::connection('mysql'), 'SELECT * FROM actor WHERE first_name = ?', ['PENEL\'OPE'], ); For more information, you can visit MySQL Visual Explain. The post Visual EXPLAIN for MySQL and Laravel appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.

Introducing Built with Laravel
Introducing Built with Laravel

Today I'm excited to share a new resource I've built for the Laravel community: BuiltWithLaravel.com, a list of companies and organizations using Laravel in the non-Laravel world. This is the list to use when you want to show someone how widespread Laravel's adoption is. The Back Story At Tighten, I spend a lot of my time meeting with potential clients. Some of our prospective partners have already committed to Laravel, so the conversation is easier. But many folks considering working with us are just kicking the tires because someone in their organization has recommended Laravel. Inevitably, someone in the organization needs convincing that Laravel is legitimate. They've heard bad things about PHP, or maybe they just have never heard of Laravel, and that's enough to scare them off. One of my key tools in this type of conversation is to show them a list of other companies that have chosen Laravel and thrived as a result of it. But that list has always been a personal list, sitting in my private notes somewhere. Earlier this year, I set out to build the ultimate list of companies using Laravel that would show our potential clients that Laravel truly is a key player in building stable, scalable web applications. And I set out to do it publicly, so I'm helping everyone, not just Tighten. BuiltWithLaravel.com After the former owner of the builtwithlaravel.com domain generously donated it to my cause (thanks Ollie!), I brought on a designer and we spent the month of July designing and building out the site. And it's live now! Built with Laravel Each organization shows you any specific technologies we know they're using, and any specific public-facing sites we know use Laravel. Sometimes we know an organization uses Laravel internally, but we can't say specifically where; they're still listed on the site, but with just a logo. Asking for Your Help I spent most of my time and energy building the site instead of the list, but here's the great news: there's a suggestion form so you can share more sites! Here are the primary criteria for being featured on this site: A company or organization that uses Laravel somewhere in the organization, even if not on their public web page The company doesn't purely exist to target the Laravel ecosystem (or if they do, they are so deeply successful it's impressive in its own right) We can publicly share attribution of how we know they're using it No packages or other code, or marketing sites for individual developers or agencies Basically: If you were trying to convince the CEO of a Silicon Valley startup or a Fortune 500 company that Laravel is legit, would this organization help convince them? If so, I want it here. Please note that I'll be manually reviewing and screenshotting sites for now, so it'll take a while to get a good workflow set up at Tighten so other folks can help me. So please be patient with any delays! Other Work If you're looking for a broader list of sites using Laravel, the closest I've found is Wire in the Wild, a list of projects and sites using Livewire, and Made with Laravel, a list of projects and sites using Laravel. I want to honor the contribution of both of those sites to our community, and also specifically name that they have a different focus: each of them highlight a huge swath of smaller projects and packages, and each of them succeed when they have more content. Built with Laravel, on the other hand, is focused on a small, hand curated list of impressive organizations. Not better, just different! So, please, check out the site, and let me know who you think we're missing! The post Introducing Built with Laravel appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.

A guide to Laravel's model events
A guide to Laravel's model events

Model events are a really handy feature in Laravel that can help you to automatically run logic when certain actions are performed on your Eloquent models. But they can sometimes lead to weird side effects if they're not used correctly. In this article, we're going to look at what model events are and how to use them in your Laravel application. We'll also look at how to test your model events and some of the gotchas to be aware of when using them. Finally, we'll take a look at some alternative approaches to model events that you might want to consider using. What are Events and Listeners? You may have already heard of "events" and "listeners". But if you haven't, here's a quick summary of what they are: Events These are things that happen in your application that you want to act on—for example, a user registering on your site, a user logging in, etc. Typically, in Laravel, events are PHP classes. Apart from events provided by the framework or third-party packages, they're usually kept in the app/Events directory. Here's an example of a simple event class that you might want to dispatch whenever a user registers on your site: declare(strict_types=1); namespace App\Events; use App\Models\User; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; final class UserRegistered { use Dispatchable; use InteractsWithSockets; use SerializesModels; public function __construct(public User $user) { // } } In the basic example above, we have an App\Events\UserRegistered event class that accepts a User model instance in its constructor. This event class is a simple data container that holds the user instance that was registered. When dispatched, the event will trigger any listeners that are listening for it. Here's a simple example of how you might dispatch that event when a user registers: use App\Events\UserRegistered; use App\Models\User; $user = User::create([ 'name' => 'Eric Barnes', 'email' => 'eric@example.com', ]); UserRegistered::dispatch($user); In the example above, we're creating a new user and then dispatching the App\Events\UserRegistered event with the user instance. Assuming the listeners are registered correctly, this will trigger any listeners that are listening for the App\Events\UserRegistered event. Listeners Listeners are blocks of code that you want to run when a specific event occurs. For instance, sticking with our user registration example, you might want to send a welcome email to the user when they register. You could create a listener that listens for the App\Events\UserRegistered event and sends the welcome email. In Laravel, listeners are typically (but not always - we'll cover this later) classes found in the app/Listeners directory. An example of a listener that sends a welcome email to a user when they register might look like this: declare(strict_types=1); namespace App\Listeners; use App\Events\UserRegistered; use App\Notifications\WelcomeNotification; use Illuminate\Support\Facades\Mail; final readonly class SendWelcomeEmail { public function handle(UserRegistered $event): void { $event->user->notify(new WelcomeNotification()); } } As we can see in the code example above, the App\Listeners\SendWelcomeEmail listener class has a handle method that accepts an App\Events\UserRegistered event instance. This method is responsible for sending a welcome email to the user. For a more in-depth explanation of events and listeners, you might want to check out the official documentation: https://laravel.com/docs/11.x/events What are Model Events? In your Laravel applications, you'll typically need to manually dispatch events when certain actions occur. As we saw in our example above, we can use the following code to dispatch an event: UserRegistered::dispatch($user); However, when working with Eloquent models in Laravel, there are some events which are automatically dispatched for us, so we don't need to manually dispatch them. We just need to define listeners for them if we want to perform an action when they occur. The list below shows the events are automatically dispatched by Eloquent models along with their triggers: retrieved - retrieved from the database. creating - model is being created. created - model has been created. updating - model is being updated. updated - model has been updated. saving - model is being created or updated. saved - model has been created or updated. deleting - model is being deleted. deleted - model has been deleted. trashed - model has been soft deleted. forceDeleting - model is being force deleted. forceDeleted - model has been force deleted restoring - model is being restored from a soft delete. restored - model has been restored from a soft delete. replicating - model is being replicated. In the list above, you may notice some of the event names are similar; for example, creating and created. The events ended in ing are performed before the action occurs and the changes are persisted in the database. Whereas the events ended in ed are performed after the action occurs and the changes are persisted in the database. Let's take a look at how we can use these model events in our Laravel applications. Listening to Model Events Using dispatchesEvents One way to listen to model events is by defining a dispatchesEvents property on your model. This property allows you to map Eloquent model events to the event classes that should be dispatched when the event occurs. This means you can then define your listeners as you would with any other event. To provide more context, let's take a look at an example. Imagine we are building a blogging application that has two models: App\Models\Post and App\Models\Author. We'll say both of these models support soft deletes. When we save a new App\Models\Post, we want to calculate the reading time of the post based on the length of the content. When we soft-delete an author, we want to soft-delete all the author's posts. Setting Up the Models We might have an App\Models\Author model that looks like so: declare(strict_types=1); namespace App\Models; use App\Events\AuthorDeleted; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; final class Author extends Model { use HasFactory; use SoftDeletes; protected $dispatchesEvents = [ 'deleted' => AuthorDeleted::class, ]; public function posts(): HasMany { return $this->hasMany(Post::class); } } In the model above, we have: Added a dispatchesEvents property that maps the deleted model event to the App\Events\AuthorDeleted event class. This means when the model is deleted, a new App\Events\AuthorDeleted event will be dispatched. We'll create this event class in a few moments. Defined a posts relationship. Enabled soft deletes on the model by using the Illuminate\Database\Eloquent\SoftDeletes trait. Now let's create our App\Models\Post model: declare(strict_types=1); namespace App\Models; use App\Events\PostSaving; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; final class Post extends Model { use HasFactory; use SoftDeletes; protected $dispatchesEvents = [ 'saving' => PostSaving::class, ]; public function author(): BelongsTo { return $this->belongsTo(Author::class); } } In the App\Models\Post model above, we have: Added a dispatchesEvents property that maps the saving model event to the App\Events\PostSaving event class. This means when the model is created or is updated, a new App\Events\PostSaving event will be dispatched. We'll create this event class in a few moments. Defined an author relationship. Enabled soft deletes on the model by using the Illuminate\Database\Eloquent\SoftDeletes trait. Our models are now prepared, so let's create our App\Events\AuthorDeleted and App\Events\PostSaving event classes. Creating the Event Classes We will create an App\Events\PostSaving event class that will be dispatched when a new post is being saved: declare(strict_types=1); namespace App\Events; use App\Models\Post; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; final class PostSaving { use Dispatchable; use InteractsWithSockets; use SerializesModels; public function __construct(public Post $post) { // } } In the code above, we can see the App\Events\PostSaving event class that accepts an App\Models\Post model instance in its constructor. This event class is a simple data container that holds the post instance that is being saved. Similarly, we can create an App\Events\AuthorDeleted event class that will be dispatched when an author is deleted: declare(strict_types=1); namespace App\Events; use App\Models\Author; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; final class AuthorDeleted { use Dispatchable; use InteractsWithSockets; use SerializesModels; public function __construct(public Author $author) { // } } In the App\Events\AuthorDeleted class above, we can see that the constructor accepts an App\Models\Author model instance. Now we can move on to creating our listeners. Creating the Listeners Let's first create a listener that can be used to calculate the estimated reading time of a post. We'll create a new App\Listeners\CalculateReadTime listener class: declare(strict_types=1); namespace App\Listeners; use App\Events\PostSaving; use Illuminate\Support\Str; final readonly class CalculateReadTime { public function handle(PostSaving $event): void { $event->post->read_time_in_seconds = (int) ceil( (Str::wordCount($event->post->content) / 265) * 60 ); } } As we can see in the code above, we've got a single handle method. This is the method that will automatically be called when the App\Events\PostSaving event is dispatched. It accepts an instance of the App\Events\PostSaving event class which contains the post that is being saved. In the handle method, we're using a naive formula to calculate the reading time of the post. In this instance, we're assuming that the average reading speed is 265 words per minute. We're calculating the reading time in seconds and then setting the read_time_in_seconds attribute on the post model. Since this listener will be called when the saving model event is fired, this means that the read_time_in_seconds attribute will be calculated every time a post is created or updated before it's persisted to the database. We can also create a listener that will soft-delete all the related posts when an author is soft-deleted. We can create a new App\Listeners\SoftDeleteAuthorRelationships listener class: declare(strict_types=1); namespace App\Listeners; use App\Events\AuthorDeleted; final readonly class SoftDeleteAuthorRelationships { public function handle(AuthorDeleted $event): void { $event->author->posts()->delete(); // Soft delete any other relationships here... } } In the listener above, the handle method is accepting an instance of the App\Events\AuthorDeleted event class. This event class contains the author that is being deleted. We're then deleting the author's posts using the delete method on the posts relationship. As a result, whenever an App\Models\Author model is soft-deleted, all the author's posts will also be soft-deleted. As a side note, it's worth noting that you'd likely want to use a more robust, reusable solution for achieving this. But for the purposes of this article, we're keeping it simple. Listening to Model Events Using Closures Another approach you can use is to define your listeners as closures on the model itself. Let's take our previous example of soft-deleting posts when an author is soft-deleted. We can update our App\Models\Author model to include a closure that listens for the deleted model event: declare(strict_types=1); namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; final class Author extends Model { use HasFactory; use SoftDeletes; protected static function booted(): void { self::deleted(static function (Author $author): void { $author->posts()->delete(); }); } public function posts(): HasMany { return $this->hasMany(Post::class); } } We can see in the model above, that we're defining our listener inside the model's booted method. We want to listen to the deleted model event, so we've used self::deleted. Similarly, if we wanted to create a listener for the created model event, we could use self::created, and so on. The self::deleted method accepts a closure which receives the App\Models\Author that's being deleted. This closure will be executed when the model is deleted, therefore deleting all the author's posts. I quite like this approach for very simple listeners. It keeps the logic inside the model class so it can be seen more easily by developers. Sometimes, extracting the logic out into a separate listener class can make the code harder to follow and track down, which can make it difficult to follow the flow of logic, especially if you're unfamiliar with the codebase. However, if the code inside these closures becomes more complex, it might be worth extracting the logic out into a separate listener class. A handy tip to know is that you can also use the Illuminate\Events\queueable function to make the closure queueable. This means the listener's code will be pushed onto the queue to be run in the background rather than in the same request lifecycle. We can update our listener to be queueable like so: declare(strict_types=1); namespace App\Models; use Illuminate\Database\Eloquent\Model; use function Illuminate\Events\queueable; final class Author extends Model { // ... protected static function booted(): void { self::deleted(queueable(static function (Author $author): void { $author->posts()->delete(); })); } // ... } As we can see in our example above, we've wrapped our closure in the Illuminate\Events\queueable function. Listening to Model Events Using Observers Another approach you can take to listen to model events is to use model observers. Model observers allow you to define all your listeners for a model in a single class. Typically, they are classes that exist in the app/Observers directory and they have methods that correspond to the model events you want to listen to. For example, if you want to listen to the deleted model event, you would define a deleted method in your observer class. If you wanted to listen to the created model event, you would define a created method in your observer class, and so on. Let's take a look at how we could create a model observer for our App\Models\Author model that listens for the deleted model event: declare(strict_types=1); namespace App\Observers; use App\Models\Author; final readonly class AuthorObserver { public function deleted(Author $author): void { $author->posts()->delete(); } } As we can see in the code above, we've created an observer that has a deleted method. This method accepts the instance of the App\Models\Author model that is being deleted. We're then deleting the author's posts using the delete method on the posts relationship. Let's say, as an example, we also wanted to define listeners for the created and updated model events. We could update our observer like so: declare(strict_types=1); namespace App\Observers; use App\Models\Author; final readonly class AuthorObserver { public function created(Author $author): void { // Logic to run when the author is created... } public function updated(Author $author): void { // Logic to run when the author is updated... } public function deleted(Author $author): void { $author->posts()->delete(); } } For the App\Observers\AuthorObserver methods to be run, we need to instruct Laravel to use it. To do this, we can make use of the #[Illuminate\Database\Eloquent\Attributes\ObservedBy] attribute. This allows us to associate the observer with the model, in a similar way to how we'd register global query scopes using the #[ScopedBy] attribute (like shown in Learn how to master Query Scopes in Laravel). We can update our App\Models\Author model to use the observer like so: declare(strict_types=1); namespace App\Models; use App\Observers\AuthorObserver; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Model; #[ObservedBy(AuthorObserver::class)] final class Author extends Model { // ... } I really like this way of defining the listener's logic because it's immediately obvious when opening a model class that it has a registered observer. So although the logic is still "hidden" in a separate file, we can be made aware that we have listeners for at least one of the model's events. Testing Your Model Events No matter which of the model event approaches you use, you'll likely want to write some tests to ensure your logic is being run as expected. Let's take a look at how we might test the model events we've created in our examples above. We'll first write a test that ensures that an author's posts are soft-deleted when the author is soft-deleted. The test may look something like so: declare(strict_types=1); namespace Tests\Feature\Models; use App\Models\Author; use App\Models\Post; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; final class AuthorTest extends TestCase { use LazilyRefreshDatabase; #[Test] public function author_can_be_soft_deleted(): void { // Create our author and post. $author = Author::factory()->create(); $post = Post::factory()->for($author)->create(); // Delete the author. $author->delete(); // Assert the author and their associated post // is soft-deleted. $this->assertSoftDeleted($author); $this->assertSoftDeleted($post); } } In the test above, we're creating a new author and a post for that author. We then soft-delete the author and assert that both the author and the post are soft-deleted. This is a really simple, yet effective, test that we can use to ensure that our logic is working as expected. The beauty of a test like this is that it should work with each of the approaches we've discussed in this article. So if you swap between any of the approaches we've discussed, your tests should still pass. Similarly, we can also write some tests to ensure the reading time of a post is calculated when the post is created or updated. The tests may look something like so: declare(strict_types=1); namespace Tests\Feature\Models; use App\Models\Author; use App\Models\Post; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; final class PostTest extends TestCase { use LazilyRefreshDatabase; #[Test] public function read_time_is_calculated_when_storing_post(): void { $post = Post::factory() ->for(Author::factory()) ->create([ 'content' => 'This is a post with some content.' ]); $this->assertSame(2, $post->read_time_in_seconds); } #[Test] public function read_time_is_calculated_when_updating_post(): void { $post = Post::factory() ->for(Author::factory()) ->create(); $post->content = 'This is a post with some content. ...'; $post->save(); $this->assertSame(8, $post->read_time_in_seconds); } } We have two tests above: The first test ensures that the reading time of a post is calculated when the post is created. The second test ensures that the reading time of a post is calculated when the post is updated. Gotchas When Using Model Events Although model events can be really handy, there are a few gotchas to be aware of when using them. The model events are only dispatched from Eloquent models. This means, that if you're using the Illuminate\Support\Facades\DB facade to interact with a model's underlying data in the database, its events won't be dispatched. For instance, take this simple example where we're deleting the author using the Illuminate\Support\Facades\DB facade: use Illuminate\Support\Facades\DB; DB::table('authors') ->where('id', $author->id) ->delete(); Running the above code would delete the author from the database as expected. But the deleting and deleted model events wouldn't be dispatched. So if you've defined any listeners for these model events when the author is deleted, they won't be run. Similarly, if you're mass updating or deleting models using Eloquent, the saved, updated, deleting, and deleted model events won't be dispatched for the affected models. This is because the events are dispatched from the models themselves. But when mass updating and deleting, the models aren't actually retrieved from the database, so the events aren't dispatched. For example, say we use the following code to delete an author: use App\Models\Author; Author::query()->whereKey($author->id)->delete(); Since the delete method is called directly on the query builder, the deleting and deleted model events won't be dispatched for that author. Alternative Approaches to Consider I like using model events in my own projects. They act as a great way of decoupling my code and also allow me to automatically run logic when I don't have as much control over the code that's affecting the model. For example, if I'm deleting an author in Laravel Nova, I can still run some logic when the author is deleted. However, it's important to know when to consider using a different approach. To explain this point, let's take a look at a basic example of where we might want to avoid using model events. Expanding on our simple blogging application examples from earlier, let's imagine we want to run the following whenever we create a new post: Calculate the reading time of the post. Make an API call to X/Twitter to share the post. Send a notification to every subscriber on the platform. So we might create three separate listeners (one for each of these tasks) that are run every time we create a new instance of App\Models\Post. But now let's look back at one of our tests from earlier: declare(strict_types=1); namespace Tests\Feature\Models; use App\Models\Author; use App\Models\Post; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use PHPUnit\Framework\Attributes\Test; use Tests\TestCase; final class AuthorTest extends TestCase { use LazilyRefreshDatabase; #[Test] public function author_can_be_soft_deleted(): void { $author = Author::factory()->create(); $post = Post::factory()->for($author)->create(); $author->delete(); $this->assertSoftDeleted($author); $this->assertSoftDeleted($post); } } If we ran the test above, when the App\Models\Post model is created via its factory, it would also trigger those three actions. Of course, calculating the read time is a minor task so it doesn't matter too much. But we don't want to be attempting to make API calls or sending notifications during a test. These are unintended side effects. If the developer writing the tests isn't aware of these side effects, it might make it harder to track down why these actions are happening. We also want to avoid having to write any test-specific logic in our listeners that would prevent these actions from running during a test. This would make the application code more complex and harder to maintain. This is one of the scenarios where you might want to consider a more explicit approach rather than relying on automatic model events. One approach could be to extract your App\Models\Post creation code up into a service or action class. For example, a simple service class may look something like so: declare(strict_types=1); namespace App\Services; use App\DataTransferObjects\PostData; use App\Models\Post; use Illuminate\Support\Str; final readonly class PostService { public function createPost(PostData $postData): void { $post = Post::create([ 'title' => $postData->title, 'content' => $postData->content, 'author_id' => $postData->authorId, 'read_time_in_seconds' => $this->calculateReadTime($postData->content), ]); $this->sendPostCreatedNotification($post); $this->publishToTwitter($post); } public function updatePost(Post $post, PostData $postData): void { $post->update([ 'title' => $postData->title, 'content' => $postData->content, 'read_time_in_seconds' => $this->calculateReadTime($postData->content), ]); } private function calculateReadTime(string $content): int { return (int) ceil( (Str::wordCount($content) / 265) * 60 ); } private function sendPostCreatedNotification(Post $post): void { // Send a notification to all subscribers... } private function publishToTwitter(Post $post): void { // Make an API call to Twitter... } } In the class above, we're manually calling the code that calculates the reading time, sends a notification, and publishes it to Twitter. This means we have more control over when these actions are run. We can also easily mock these methods in our tests to prevent them from running. We still also have the benefit of being able to queue these actions if we need to (which we likely would in this scenario). As a result of doing this, we can remove the use of the model events and listeners for these actions. This means we can use this new App\Services\PostService class in our application code, and safely use the model factories in our test code. A bonus of doing this is that it can also make the code easier to follow. As I've briefly mentioned, a common criticism of using events and listeners is that it can hide business logic in unexpected places. So if a new developer joins the team, they may not know where or why certain actions are happening if they're triggered by a model event. However, if you would still like to use events and listeners for this kind of logic, you could consider using a more explicit approach. For example, you could dispatch an event from the service class that triggers the listeners. This way, you can still use the decoupling benefits of events and listeners, but you have more control over when the events are dispatched. For example, we could update the createPost method in our App\Services\PostService example above to dispatch an event: declare(strict_types=1); namespace App\Services; use App\DataTransferObjects\PostData; use App\Events\PostCreated; use App\Models\Post; use Illuminate\Support\Str; final readonly class PostService { public function createPost(PostData $postData): void { $post = Post::create([ 'title' => $postData->title, 'content' => $postData->content, 'author_id' => $postData->authorId, 'read_time_in_seconds' => $this->calculateReadTime($postData->content), ]); PostCreated::dispatch($post); } // ... } By using the approach above, we could still have separate listeners to make the API request to Twitter and send the notification. But we have more control over when these actions are run so they aren't run inside our tests when using model factories. There aren't any golden rules when deciding to use any of these approaches. It's all about what works best for you, your team, and the feature you're building. However, I tend to follow the following rules of thumb: If the action in the listener is only making minor changes to the model, consider using model events. Examples: generating slugs, calculating read times, etc. If the action is going to affect another model (whether that be automatically creating, updating, or deleting), then be more explicit and don't use model events. If the action is going to be working with external processes (API calls, file handling, triggering notifications, queued jobs), then be more explicit and don't use model events. Pros and Cons of Using Model Events To quickly summarise what we've covered in this article, here's a simple list of pros and cons of using model events: Pros Encourages you to decouple your code. Allows you to automatically trigger actions no matter where the model was created/updated/deleted. For example, you can trigger business logic if the model was created in Laravel Nova. You don't need to remember to dispatch the event every time you create/update/delete a model. Cons Can lead to unintended side effects. You may want to create/update/delete a model without triggering some of the listeners, but this might lead to unexpected behaviour. This can be particularly problematic when writing tests. Can hide business logic in unexpected places that's hard to track down. This can make the flow of your code harder to follow. Conclusion Hopefully, this article has given you an overview of what model events are and the different ways to use them. It should have also shown you how to test your model event code and some of the gotchas to be aware of when using them. You should hopefully now feel confident enough to make use of model events in your Laravel apps. The post A guide to Laravel's model events appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.

VS Code Snippets for Livewire and Alpine.js
VS Code Snippets for Livewire and Alpine.js

VS Code - as many code editors do - includes a feature called "snippets". The feature allows you to configure shortcuts for pieces of code that you use often. For example, you could set up a snippet so that you type a few characters like fun, press tab, and it'll expand into a whole function declaration. I've recently been making another round at optimizing my VS Code setup, so I skimmed through Caleb Porzio's Make VS Code Awesome course again. It reminded me how useful and powerful these snippets can be, so I started thinking about new ones I could add that would make my life easier. Today, I'll share some of those with you and maybe you can take a few away that will be useful to your workflow. Specifically, I'll be sharing ones I use when developing Livewire and Alpine.js apps. How to configure snippets in VS Code To start off, how do you configure snippets in VS Code? If you already know how, you can skip this section and get to the good part. First, open the Command Pallette by pressing cmd+shift+p on Mac or ctrl+shift+p on Windows. Start typing in "Snippets" and it should filter down to an option called "Snippets: Configure User Snippets". Select that option, then it will prompt you to select the language you want to configure snippets for. Following the basic example I mentioned earlier, if you select "javascript.json (JavaScript)" and paste the following snippet inside, you should have successfully configured a "function" snippet that'll be available when you're writing in a JS file. { "Function": { "prefix": "fun", "body": [ "function $1($2) {", " $3", "}" ] }, } The configuration of snippets is relatively simple. Each snippet is a JSON object. The key of the object is the snippet name. The prefix inside is the shortcut you'll use to insert the snippet into your code. The body is an array of lines that will be inserted as part of the snippet. You can also include placeholders in the snippet by using $1, $2, $3, etc. After you insert the snippet, you can continue pressing tab and it'll move your cursor to each of the placeholders. In this example, the placeholders make it really easy to add the function name, parameters, then body. PHP/Livewire Now that you know how to configure a snippet let's go over some of my favorites for PHP and Livewire classes. These can be configured by selecting "php.json (PHP)" in the Command Pallette. Class properties and methods cover the basics, but I recent added snippets for Livewire's mount method, #[Computed] properties (including the PHP attribute), and #[Url] properties. { "Public Property": { "prefix": "pub", "body": [ "public ${0};" ], "description": "PHP Public Property" }, "Computed Property": { "prefix": "comp", "body": [ "#[Computed]", "public function $1()", "{", " $2", "}" ] }, "Livewire Url Property": { "prefix": "url", "body": [ "#[Url$3]", "public $1 = $2;", ] }, "Livewire Mount Method": { "prefix": "mount", "body": [ "public function mount()", "{", " $1", "}" ] }, "Method": { "prefix": "met", "body": [ "public function $1($2)", "{", " $3", "}" ] }, "Private Method": { "prefix": "pmet", "body": [ "private function ${1}(${2})", "{", " ${0}", "}" ], "description": "PHP private function" }, } Blade and Alpine.js Some of my favorites for Blade and Alpine snippets are: con for console.log raw for console.log with the value wrapped in Alpine.raw (a tip I covered in a recent article) tailwindcdn for the Tailwind CDN script which is useful for quickly setting up Tailwind for demos and testing ideas out These can be configured by selecting "blade.json (Blade)" in the Command Pallette. { "Console Log": { "prefix": "con", "body": [ "console.log($1)" ] }, "Console Log (Alpine.raw)": { "prefix": "raw", "body": [ "console.log(Alpine.raw($1))" ] }, "Tailwind Play CDN": { "prefix": "tailwindcdn", "body": [ "<script src=\"https://cdn.tailwindcss.com\"></script>" ] }, } If you're using Livewire Volt's class API, you might find it useful to include you normal PHP snippets in the Blade configuration as well. I hope you were able to find some of the snippets useful or at least got some inspiration for snippets that will benefit your own workflow! Again, if you haven't checked out Caleb's Make VS Code Awesome course yet, I highly recommend it. It includes a bunch of other snippets that I didn't cover here, plus theming, keyboard shortcuts, extensions, and more. The post VS Code Snippets for Livewire and Alpine.js appeared first on Laravel News. Join the Laravel Newsletter to get Laravel articles like this directly in your inbox.