Understanding Code Smells in PHP and Laravel: A Beginner's Guide

WhoAmI => notes.sohag.pro/author
Search for a command to run...

WhoAmI => notes.sohag.pro/author
No comments yet. Be the first to comment.
Understanding Laravel Macros Have you ever wished you could add your own special features to Laravel's built-in functions? That's exactly what Laravel macros let you do! Think of macros as custom add-ons that enhance Laravel's capabilities without me...
I had a solid list of reasons my life wasn't moving faster, until a grainy old lecture pointed out the one name missing from it

The finale isn't a victory lap. It's the story of the control I shipped that did nothing, the footgun still sitting in my demo, and the handful of things I'd keep exactly as they are.

How do you hold a large payment for a second pair of eyes without ever letting the unapproved money touch a balance, and how do you stream that decision to the outside world without standing up a broker?

How do you show the total under a parent account when the whole system refuses to store a balance? A recursive query, a trigger that refuses to draw a circle, and a rule about what actually has to sum to zero.

Every time I wanted to change an FX rate I had to edit a file on the server and restart the app. So I moved rates and markup into a live admin API, and then audited it hard enough to find the bug that quietly undid the whole thing.

Ever walked into a room and immediately noticed something was off? Maybe it was a faint burning smell from the kitchen or the sound of a washing machine that didn't quite seem right. These warning signs in our daily lives are similar to what developers call "code smells" in programming—hints that something in our code needs attention.
Code smells are warning signs in our code that suggest deeper problems. They're not bugs—the code might work perfectly fine—but they indicate areas where your code could be cleaner, more efficient, or easier to maintain.
Think of it like a cluttered garage. Everything works and you can still park your car, but finding your tools takes longer than it should, and adding new items becomes increasingly difficult. That's exactly how code with "smells" feels to work with.
This is like trying to read an instruction manual with no chapters or sections—it's overwhelming and hard to follow.
public function processOrder($orderId)
{
$order = Order::find($orderId);
// Validate order
if (!$order) {
throw new Exception('Order not found');
}
// Check inventory
foreach ($order->items as $item) {
$product = Product::find($item->product_id);
if ($product->stock < $item->quantity) {
throw new Exception('Insufficient stock');
}
}
// Update inventory
foreach ($order->items as $item) {
$product = Product::find($item->product_id);
$product->stock -= $item->quantity;
$product->save();
}
// Process payment
$payment = new Payment();
$payment->amount = $order->total;
$payment->order_id = $order->id;
$payment->process();
// Send email
Mail::to($order->user->email)->send(new OrderConfirmation($order));
// Update order status
$order->status = 'processed';
$order->save();
}
public function processOrder($orderId)
{
$order = $this->findOrder($orderId);
$this->validateInventory($order);
$this->updateInventory($order);
$this->processPayment($order);
$this->sendConfirmation($order);
$this->updateOrderStatus($order);
}
private function findOrder($orderId)
{
$order = Order::findOrFail($orderId);
return $order;
}
private function validateInventory(Order $order)
{
foreach ($order->items as $item) {
if ($item->product->stock < $item->quantity) {
throw new Exception('Insufficient stock');
}
}
}
// Additional methods...
Imagine having the same house key copied five times—if you need to change the lock, you'll have to replace all five keys. That's the problem with code duplication.
public function calculateTotalPrice($items)
{
$total = 0;
foreach ($items as $item) {
$price = $item->price;
$tax = $price * 0.2;
$shipping = $price > 100 ? 0 : 10;
$total += $price + $tax + $shipping;
}
return $total;
}
public function calculateDiscountedPrice($items)
{
$total = 0;
foreach ($items as $item) {
$price = $item->price;
$tax = $price * 0.2;
$shipping = $price > 100 ? 0 : 10;
$discount = $price * 0.1;
$total += $price + $tax + $shipping - $discount;
}
return $total;
}
private function calculateBasePrice($item)
{
$price = $item->price;
$tax = $price * 0.2;
$shipping = $price > 100 ? 0 : 10;
return $price + $tax + $shipping;
}
public function calculateTotalPrice($items)
{
return collect($items)->sum(function ($item) {
return $this->calculateBasePrice($item);
});
}
public function calculateDiscountedPrice($items)
{
return collect($items)->sum(function ($item) {
$basePrice = $this->calculateBasePrice($item);
return $basePrice - ($item->price * 0.1);
});
}
This is like having one kitchen drawer that holds everything from utensils to receipts to batteries. In Laravel, it often manifests as a model that knows too much and does too much.
class User extends Model
{
public function processOrder($items)
{
// Order processing logic
}
public function calculateTaxes()
{
// Tax calculation logic
}
public function sendWelcomeEmail()
{
// Email logic
}
public function generateInvoice()
{
// Invoice generation logic
}
public function updateShippingAddress($address)
{
// Address update logic
}
}
class User extends Model
{
public function orders()
{
return $this->hasMany(Order::class);
}
}
class OrderProcessor
{
public function process(User $user, array $items)
{
// Order processing logic
}
}
class TaxCalculator
{
public function calculate(User $user)
{
// Tax calculation logic
}
}
class UserMailer
{
public function sendWelcome(User $user)
{
// Email logic
}
}
Addressing code smells isn't just about being pedantic—it has real, practical benefits:
Reduced Bug Risk: Cleaner code means fewer hiding places for bugs. When each piece of code has a single, clear responsibility, problems are easier to spot and fix.
Easier Maintenance: Think of clean code like a well-organized toolbox. When you need to fix something, you know exactly where to look.
Better Team Collaboration: New team members can understand and work with clean code more quickly, reducing onboarding time and friction.
Lower Technical Debt: By addressing code smells early, you prevent small issues from snowballing into major refactoring projects.
Use Laravel's built-in tools to fight code smells:
Leverage Service Providers for dependency injection
Use Form Requests for validation logic
Implement Jobs for complex processing
Utilize Events and Listeners for decoupling
Follow Laravel's conventions:
// Instead of this:
public function get_user_posts($user_id)
// Do this:
public function getUserPosts($userId)
Code smells are warning signs, not errors. They indicate areas where your code could be improved.
Regular refactoring is like regular house cleaning—it's easier to maintain cleanliness than to deal with accumulated mess.
Use Laravel's built-in features and conventions to write cleaner code from the start.
When in doubt, follow the Single Responsibility Principle: each class and method should do one thing and do it well.
Begin your code cleanup journey by:
Reviewing one piece of code at a time
Looking for the smells we've discussed
Making small, incremental improvements
Running tests after each change
Committing improvements regularly
Remember, perfect code doesn't exist, but better code always does. Start small, be consistent, and gradually build better coding habits. Your future self (and your teammates) will thank you!