DEV Community

Cover image for Writing Code like a Senior Developer in Laravel
Martin Tonev
Martin Tonev

Posted on

Writing Code like a Senior Developer in Laravel

In the evolving landscape of web development, Laravel has emerged as one of the prominent PHP frameworks for building elegant applications. If you’re looking to elevate your coding to the senior developer level, it’s crucial not only to understand Laravel’s features but also to know how to use them effectively. This article delves into best practices, and common pitfalls, and provides both wrong and right coding examples to guide you in writing clean, efficient, and scalable code in Laravel.

Understanding Laravel

Before jumping into the code, it’s essential to have a solid understanding of Laravel’s ecosystem, including its MVC architecture, ORM (Eloquent), Blade templating, and other components like migrations, seeders, and middleware. Laravel is designed to make common tasks such as routing, authentication, sessions, and caching easier and more intuitive.

Writing Clean and Efficient Code

  1. Follow Laravel Naming Conventions

Wrong Way:

Inconsistent and unclear naming:

class orderController extends Controller {
    public function get_data_by_id($id) {
        // ...
    }
}

Enter fullscreen mode Exit fullscreen mode

Right Way:

Following Laravel’s naming conventions and PSR standards:

class OrderController extends Controller {
    public function getDataById($id) {
        // ...
    }
}


// Using Resourceful Controllers:
Route::resource('orders', 'OrderController');
Enter fullscreen mode Exit fullscreen mode

2. Utilize Eloquent ORM Effectively

Wrong Way:

Using raw queries or not leveraging Eloquent’s potential:

$users = DB::table('users')->where('votes', '>', 100)->get();
Enter fullscreen mode Exit fullscreen mode

Right Way:

Using Eloquent’s ORM for readability and maintainability:

$users = User::where('votes', '>', 100)->get();
Enter fullscreen mode Exit fullscreen mode

3. Leverage Relationships in Eloquent

Wrong Way:

Ignoring Eloquent relationships and manually joining tables:

$books = DB::table('books')
         ->join('authors', 'authors.id', '=', 'books.author_id')
         ->select('books.*', 'authors.name as author_name')
         ->get();

Enter fullscreen mode Exit fullscreen mode

Right Way:

Defining and using Eloquent relationships:

class Book extends Model {
    public function author() {
        return $this->belongsTo(Author::class);
    }
}

// Then you can easily get books with authors:
$books = Book::with('author')->get();
Enter fullscreen mode Exit fullscreen mode
4. DRY Principle: Don’t Repeat Yourself
Enter fullscreen mode Exit fullscreen mode

Wrong Way:

Repeating code in multiple methods or controllers:

public function show($id) {
    $item = Item::find($id);
    $categories = Category::all();
    return view('item.show', compact('item', 'categories'));
}

public function edit($id) {
    $item = Item::find($id);
    $categories = Category::all();
    return view('item.edit', compact('item', 'categories'));
}
Enter fullscreen mode Exit fullscreen mode

Right Way:

Extracting repeated code into a single method or service:

private function getItemAndCategories($id) {
    $data['item'] = Item::find($id);
    $data['categories'] = Category::all();
    return $data;
}

public function show($id) {
    $data = $this->getItemAndCategories($id);
    return view('item.show', $data);
}

public function edit($id) {
    $data = $this->getItemAndCategories($id);
    return view('item.edit', $data);
}
Enter fullscreen mode Exit fullscreen mode
5. Handle Business Logic in Services (very important)

Enter fullscreen mode Exit fullscreen mode

Wrong Way:

Placing complex business logic directly in controller methods:

public function store(Request $request) {
    // Validating request and handling complex business logic
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Right Way:

Using service classes to handle business logic:

class OrderService {
    public function placeOrder($data) {
        // Handle order placement logic
    }
}

// In Controller
public function store(Request $request, OrderService $orderService) {
    $orderService->placeOrder($request->all());
    // ...
}

Enter fullscreen mode Exit fullscreen mode

Optimization and Best Practices

Use Artisan Commands: Laravel’s Artisan command-line tool offers a plethora of commands to speed up development, from generating boilerplate code to managing database migrations.
Database Migrations and Seeders: Leverage migrations for database version control and seeders to populate your database with initial or dummy data.
Middleware for Reusability: Implement middleware for cross-cutting concerns like logging, authentication, and caching.
Automated Testing: Write tests using PHPUnit or Laravel’s built-in testing features to ensure your application is reliable and maintainable.
Queue Jobs for Long Processes: Utilize Laravel’s queue system to handle time-consuming tasks like sending emails or processing images asynchronously.
Regularly Update Dependencies: Keep your Laravel application and its dependencies up to date to benefit from the latest features, performance improvements, and security patches.

Image description

Subscribe to our “Laravel SaaS starter package”

Conclusion

Evolving from a junior to a senior Laravel developer involves a deep understanding of the framework, writing clean and maintainable code, and constantly learning and adapting to new practices. By following the guidelines and examples provided in this article, you can write code that is not only functional but also elegant and efficient, truly reflecting the capabilities of a senior Laravel developer. Remember, being “senior” is not just about years of experience but the quality and craftsmanship of your work. Happy coding!

Top comments (20)

Collapse
 
jesusantguerrero profile image
Jesus Guerrero

We should take care before labeling something as wrong without giving context because there's a reason things are there in the first place

Eg. With eloquent vs DB::table(). when you want granular data and calculations with select with the eloquent model the attribute with the same name will be overwritten and are Eloquent is slower for some cases DB::table gives you more control an its more suitable.

Collapse
 
superloika profile image
Kaloy

Agree. Using the query builder shouldn't be labeled as the wrong way. I think the combination of both is the right way and it should be depending on the use case e.g. use query builder for large datasets.

Collapse
 
martintonev profile image
Martin Tonev

There are cases where I even write plain MySQL but the point is for juniors to use Models and relations.

Because I have been part of large projects where juniors use only DB - you can imagine if something changes in one table, how many places you have to change it OR just add one global scope to table.

Collapse
 
leob profile image
leob • Edited

Good article, but if this would already be enough to make you or me a Laravel Senior then we'd be setting the bar pretty low :P ...

There's such enormous breadth and depth to the way you can do things in Laravel - but of course that would require not a single article on dev.to, but a series, or a whole book ...

Collapse
 
martintonev profile image
Martin Tonev

All the things which senior need to know are things for book not for article.

Collapse
 
danielhe4rt profile image
Daniel Reis

I would not say that is a "Senior" thing. It's the basics from the Framework itself.

There's so many ways that you can do the same thing in Laravel, using features that isn't even documented that you would be impressed.

Totally agree with the SOLID, DRY and naming classes etc but we as seniors should know that DEPENDS mostly on how much time we have and what the feature ask us.

And btw: do not user $request->all() if you're validating something when you can go for $request->validated() :p

Collapse
 
martintonev profile image
Martin Tonev

All the things which senior need to know are things for book not for article.

Collapse
 
roestvrijstaal profile image
RoestVrijStaal • Edited

Using the Query Builder instead of Eloquent is not "wrong". It depends per (company) context of what is the most usable.

However, most "plugins" and "extensions" of the Laravel ecosystem are so opinionated that they assume the use of Eloquent.

When the use of Eloquent or another ORM is not needed, then Laravel is a questionable choice to use as framework.

The use of Laravel's own database migrations and seeders is also context-dependent.

Also, the use of Middleware should be restricted to the bare necessaries.

Often I see people creating Middleware for certain parts of their application. They forget that EACH REQUEST goes through their Middleware (when no strict checks are put in their Middleware's provider, which is often forgotten as well). Most of the time it was better that the logic of that Middleware was moved to a trait. To be used by the controllers of those parts of their application only.

For the rest of the article mostly programming best practices are highlighted. The use of PSR's should be no subject of discussion. But following and using them does not make you a Senior Laravel Developer at all.

Collapse
 
devtronic profile image
Julian Finkler

Wrong and right are terms that should a senior developer avoid in a context like this.
Applying the DRY principle everywhere causes tighter coupling. You have to use the most fitting tool - or are you using a hammer for screws too? 😅

Collapse
 
msamgan profile image
Mohammed Samgan Khan

This was good but was pretty generic. But Nice for covering basics.

Collapse
 
rickrickiin profile image
Rickrickiin

This post will definitely be helpful.

Collapse
 
iusting20 profile image
Iustin Ghergu

Leveraging Eloquent relationships is a game-changer! Especially if you have a well-mapped UML diagram before diving into coding them!

Collapse
 
teamradhq profile image
teamradhq

I feel like the examples you've provided are more like Junior developer oopsies as opposed to senior developer pro tips.

For example, why wouldn't a senior developer use the DB facade? There are things you can't do with Eloquent that are easily achieved with the facade. And there are things that you might not want to define model methods for.

Here's a silly example: say you wanted to get a list of authors and illustrators who share the same birthday (a non-indexed column), you wouldn't want to define a relationship for this:

DB::table('authors')
    ->join('illustrators', 'authors.id.birthday', '=', 'illustrators.birthday')
    ->select('authors.name as author_name, illustrators.name as illustrator_name')
    ->get()
Enter fullscreen mode Exit fullscreen mode

There are good reasons for using the DB facade here:

  • This is not a relationship in the strictest sense. At most, the query provides a coincidental relationship.
  • This information is unlikely to be used in other parts of the application.
  • If this were a model method, then developers would use it.

And your DRY tips seem a bit off... I'd expect a senior Laravel talking about this topic to discuss traits at the very least:

trait HasBirthday {
    public function scopeBornOn(Builder $query, Carbon|String $date) {
        return $query->where('birthday', $date);
    }
}

class User extends Model {
    use HasBirthday;
}

class Author extends Model {
    use HasBirthday;
}

class Illustrator extends Model {
    use HasBirthday;
}

User::bornOn('2008-01-01')->get();
Author::bornOn('2008-01-01')->get();
Illustrator::bornOn('2008-01-01')->get();
Enter fullscreen mode Exit fullscreen mode

And while your example, provides a good start for a Junior developer, a senior developer would write this more succinctly:

private function getItemAndCategories(int $id) {
    $data['item'] = Item::find($id);
    $data['categories'] = Category::all();
    return $data;
}
Enter fullscreen mode Exit fullscreen mode

Because there's no point in having an intermediary variable here:

private function getItemAndCategories(int $id): array {
    return [
        'item' => Item::find($id),
        'categories' => Category::all(),
    ];
}
Enter fullscreen mode Exit fullscreen mode

Or here:

public function show(int $id) {
    return view('item.show', $this->getItemAndCategories($id));
}
Enter fullscreen mode Exit fullscreen mode

And would a senior developer ever call Model::all like this? At the very least, a senior developer would paginate the result and restrict the columns:

Category::paginate(25, ['name', 'id', 'description'], 'categories');
Enter fullscreen mode Exit fullscreen mode

If you have hundreds of categories, you don't return every one and you don't return all data.

And where are the examples for Artisan, Migrations, Seeders, Middleware, Testing, and Queues? If you scrubbed all of your examples and provided some context and examples (with syntax highlighting) on these topics, the article would be more worthy of its title :)

Collapse
 
anilkumarthakur60 profile image
anil kumar thakur
Collapse
 
jonrandy profile image
Jon Randy 🎖️

AI generated/assisted posts should adhere to the guidelines for such content.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.