Frameworks such as Symfony (7.2 at the time of writing) or Laravel are highly customizable and encourage good practices, regardless of your experience and skills.
However, you may still introduce design, security, or performance issues.
Symfony: don't call the $container
directly
❌ This one is a classic but still heavily used by developers:
class LuckyController extends AbstractController
{
public function index()
{
$myDependency = $this->container->get(MyDependencyInterface::class);
//
}
It's possible because the parent AbstractController
defines $container
as protected:
protected ContainerInterface $container;
While it does work, it's a bad practice for various reasons:
- it harms readability
- it's harder to test
- it relies on global states (
$container
) - it could lead to incompatibility issues in the future, as Symfony evolves
✅ Use dependency injection in the constructor instead:
class LuckyController extends AbstractController
{
public function __construct(private MyDependencyInterface $myDependency) {}
Eloquent ORM: don't use raw queries blindly
Eloquent allows writing SQL queries quite conveniently.
Instead of writing SQL queries directly, developers can use PHP wrappers to interact with database entities.
It also uses SQL bindings behind the scene, so you get injection protection for free, even with untrusted inputs:
User::where('email', $request->input('email'))->get();
❌ However, when you use helpers like whereRaw
, you may introduce vulnerabilities:
User::whereRaw('email = "'. $request->input('email'). '"')->get();
✅ At least, always use SQL bindings:
User::whereRaw('email = :email', ['email' => $request->input('email')])->get();
N.B.: the above example does not make sense, but it keeps things simple. In real-world use cases, you may need whereRaw
for optimization purposes or to implement very specific where
conditions.
Laravel: what about CSRF?
With CSRF attacks, hackers force the end users to execute unwanted actions on an app in which they're currently authenticated.
Laravel has a built-in mechanism to guard against such scenario.
Roughly speaking, it adds a token (hidden field) to be sent along with your requests, so you can verify that "the authenticated user is the person actually making the requests to the application."
Fair enough.
❌ However, some apps skip this implementation.
✅ Whether you should use the built-in middleware is not relevant here, but ensure your app is secured against CSRF attacks.
You may read this page for more details about the implementation in Laravel.
Please secure AJAX requests too.
Eloquent ORM: queries are not optimized "automagically"
Eloquent allows eager/lazy loading, and supports various optimizations, such as query caching, indexing, or batch processing.
However, it does not prevent all performance issues, especially on large datasets.
❌ It's not uncommon to see such loops:
$users = User::all();
foreach ($users as $user) {
// some code
}
But it can lead to memory issues.
✅ When possible, a better approach would leverage Laravel collections and helpers like chunk
:
User::chunk(200, function (Collection $users) {
foreach ($users as $user) {
// ...
}
});
Check the documentation for more details.
N.B.: It works, but don't forget those helpers are only meant to ease the implementation, so you have to monitor slow queries and other bottlenecks.
Symfony: SRP services
As the documentation says:
useful objects are called services and each service lives inside a very special object called the service container. The container allows you to centralize the way objects are constructed. It makes your life easier, promotes a strong architecture and is super fast!
In other words, you will write custom services to handle specific responsibilities for your application.
❌ The documentation is right. It aims to promote a strong architecture, but it's not uncommon to read services that break the Single Responsibility Principle (SRP):
class OverComplexService
{
public function __construct(
private ProductRepository $productRepository,
private InvoiceRepository $invoiceRepository,
private EmailService $emailService,
);
}
✅ You must respect that principle. It will be easier to test and maintain.
Symfony: public vs. private services
❌ With Symfony, you can use $container->get('service_id')
to call any public service.
We saw earlier that calling the $container
like that is considered as a bad practice.
You may not resist the temptation to make all services public, so you can retrieve them by their ID almost everywhere in the project.
Don't do that.
✅ Instead, keep most custom services private and use dependency injection.
Wrap up
Hopefully, you will avoid what I like to call "latent errors" or "hidden errors" with frameworks.
If you don't know how to implement it, trust the framework, but be aware some mechanisms may not be enabled by default.
Top comments (0)