Single Responsibility Principle In PHP (SRP)
Since I started programming never cared about SOLID PRINCIPLES that much. Because I wasn't aware of all these things. I started by watching YouTube v...
Since I started programming never cared about SOLID PRINCIPLES that much. Because I wasn't aware of all these things. I started by watching YouTube videos and maybe because of that it wasn't mentioned by anyone.
But now, when I'm exploring a few open-source projects and learning day by day to improve myself. I'm thinking about to deep dive into the concepts.
Today we are going to learn about the Single Responsibility Principle In PHP. How does it work, what does it mean also we are going to create a practical example.
What Is The Single Responsibility Principle
First, we need to understand what this principle does. Because for me it was never easy to understand things just by theory. I'm more of a practical guy, and usually love doing things by implementation. But still, we need some theory to understand the definition.
Let's take an example again.
Imagine you're at a restaurant, and there's a waiter who takes your order, cooks the food, serves the food, and even washes the dishes. This is inefficient and error-prone because the waiter is trying to do too many things at once.
Now, think of a more organized restaurant:
- A waiter takes your order.
- A chef cooks the food.
- Another waiter serves the food.
- A dishwasher cleans the dishes.
Each person has a single responsibility. If there's a change in the menu, only the chef needs to adapt. If there's a new serving style, only the waiter needs to change how they serve. This makes the restaurant more efficient and easier to manage.
SRP In the Programming World
Single Responsibility: A class should only do one thing. If it does more than one thing, it's harder to maintain and understand.
Reason to Change: If a class has only one job, it will only change for one reason. This makes the code easier to manage and debug.
Practical Example
Let's take an Unorganized code exmple, now this code does a lot of work in the same class and User Class
is responsible of doing so many things.
- Building Database Connection
- Storing User
- Authenticating User
I have generated this code from chat Gpt. But can be a good idea to understand the principle. See, This class is doing three seprate action. Maybe we can use User Class in all over the application in just to show data and store user. But Authentication is a seprate thing part of a seprate module and it should be managed seprately. I hope you get the idea :). It should have it's own responsiblity to Authenticated the User. Having said that, Storing, Deleting etc. should be handled by User Class.
<?php
class User {
public $id;
public $username;
public $password;
public $email;
public function __construct($id, $username, $password, $email) {
$this->id = $id;
$this->username = $username;
$this->password = $password;
$this->email = $email;
}
public function save() {
$conn = new mysqli("localhost", "root", "", "test_db");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$stmt = $conn->prepare("INSERT INTO users (username, password, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $this->username, $this->password, $this->email);
if ($stmt->execute() === TRUE) {
echo "New record created successfully";
} else {
echo "Error: " . $stmt->error;
}
$stmt->close();
$conn->close();
}
public function authenticate($username, $password) {
$conn = new mysqli("localhost", "root", "", "test_db");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
return new User($user['id'], $user['username'], $user['password'], $user['email']);
} else {
return null;
}
$stmt->close();
$conn->close();
}
}
now let's organize this code in different class and manage accordingly.
Tasks to Apply SRP
- Database Connection Handling: Move the database connection logic to its own class.
- User Authentication Logic: Separate the logic for authenticating users into its own class.
- User Data Representation: Keep the
User
class focused only on representing user data and storage.
Database Connection Class
<?php
class Database {
private $host = "localhost";
private $username = "root";
private $password = "";
private $database = "test_db";
private $conn;
public function connect() {
$this->conn = new mysqli($this->host, $this->username, $this->password, $this->database);
if ($this->conn->connect_error) {
die("Connection failed: " . $this->conn->connect_error);
}
return $this->conn;
}
public function disconnect() {
if ($this->conn) {
$this->conn->close();
}
}
}
?>
User Authentication
<?php
class UserAuthentication {
private $db;
public function __construct(Database $db) {
$this->db = $db;
}
public function authenticate($username, $password) {
$conn = $this->db->connect();
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
return new User($user['id'], $user['username'], $user['password'], $user['email']);
} else {
return null;
}
$stmt->close();
$this->db->disconnect();
}
}
?>
User Class
<?php
class User {
public $id;
public $username;
public $password;
public $email;
public function __construct($id, $username, $password, $email) {
$this->id = $id;
$this->username = $username;
$this->password = $password;
$this->email = $email;
}
public function save(){} //save the user
public function delete(){} //delete the user
public funciton update(){} //update the user
}
?>
Conclusion
Finally, I am assuming that you have understood the point that I was trying to say. The whole idea is that the class should only take care of one responsibliy. User is common but authentication and usualy process like store and delete is a seprate thing in terms of managing action wise. So, this point can be different in others perspective :). But having less clutter in a class makes much more sense. I hope this article will help you somewhere in your journey in becoming best dev. If you got any question or discussion let's meet in the comments below.
You may find this helpful: Repository pattern in PHP
Or you can join the discord channel. Larachamp Discord