StageLink
Internship management platform, built on hand-written PHP MVC.
CESI · Web development · Team project

- Role
- Controllers, models and integration
- Team
- 4 developers
- User types
- Student, programme lead, administrator
- No framework
- Router, MVC and data access written by hand
Key points
- Router and MVC layer written by hand, without a framework
- Three user profiles with distinct rights
- Wish list, application with CV and cover letter, response tracking
- Prepared statements throughout, view escaping handled by Twig
The walkthrough
How it works
- 1
The URL is decoded
Every request enters through a single file. It rebuilds the route from the URL, both the readable `/StageLinkV1/offers` form and the legacy query-parameter form, then checks that a session exists. Without one, and outside a few public routes, the request is sent back to the login page before any business code runs.
- 2
The controller takes over
Each route names a controller: offers, companies, wish list, dashboard, users. It reads the parameters, decides what to ask the model for, and holds neither SQL nor HTML. That discipline is what makes the MVC pattern legible when you write it yourself rather than inherit it.
- 3
The model queries the database
Models carry data access and business logic. Every query goes through prepared statements, more than eighty across the project, which rules out SQL injection by construction rather than by vigilance.
- 4
Twig renders the page
Templates receive ready-made data and only lay it out. Output escaping is Twig's own, on by default: protection against script injection comes from the engine, not from a call someone might forget.
What the platform does
StageLink brings together what is usually scattered: internship offers, partner company profiles and application tracking. A student searches offers by keyword, skill or duration, adds one to a wish list, applies with a CV and cover letter, then follows the state of each reply. A programme lead manages offers, companies and their cohort's accounts, and monitors how the search is going. An administrator manages the leads themselves. Companies can be rated, and offers aggregated into statistics by skill or duration.
Context
CESI students look for internships through personal networks, with no shared entry point. The brief asked for a complete web application built without a framework: no Symfony, no Laravel, only PHP, a template engine and a MySQL database. That constraint is the point of the project, it forces you to write the routing, the separation of concerns and the data access yourself.
Security: what is in place, and what is missing
Rereading the code for this page, I preferred to separate the two. A list of good practices is easy to write; what matters is what is actually there.
- In place, passwords hashed with `password_hash`, which applies bcrypt
- In place, prepared statements across every database access
- In place, automatic variable escaping by Twig, with two explicit exceptions on pagination
- Missing, no anti-CSRF token on forms
- Missing, no session ID regeneration after login, so no protection against session fixation
What I took away
- The MVC pattern understood from the inside, without framework magic
- Designing a relational schema and a per-profile rights model
- What a framework actually buys you, rereading this code two years on shows it better than any lecture
- Version control and teamwork on a shared codebase
Gallery
1/6 · Internship offer list





