Create a Simple Class with Properties and Methods
Learning Objectives
- Understand OOP principles in PHP
- Create and use classes and objects
- Implement inheritance and polymorphism
- Apply OOP best practices
What We're Building
Welcome to our tutorial on creating a simple class with properties and methods! In this session, we'll implement the assigned homework by building a Product class that could be used in an e-commerce WordPress website or plugin. This class will demonstrate the fundamental concepts of Object-Oriented Programming (OOP) that we've been learning: properties, methods, constructors, and more.
By the end of this tutorial, you'll have created a fully functional class that can be used to represent products in a store, complete with properties to store product information and methods to perform common operations.
Understanding the Problem
Our homework assignment is to create a simple class with properties and methods. Let's break down what this means:
We need to create a class that:
- Has several properties to store information about products
- Includes a constructor to initialize those properties
- Provides methods to perform operations on products
- Incorporates different types of access modifiers (public, protected, private)
For our e-commerce context, we'll need properties to store product details like ID, name, price, etc., and methods to perform operations like checking stock, updating prices, etc.
Planning the Solution
Using George Polya's 4-step problem-solving method, let's develop a plan for our class:
Step 1: Understand the Problem
We need to create a Product class with:
- Properties to store product information
- Constructor to initialize these properties
- Methods to perform operations on the products
- Different access modifiers for encapsulation
Step 2: Devise a Plan
Here's our simplified whiteboard plan:
- Define the class name and its properties with appropriate access modifiers
- Create a constructor to initialize the properties
- Implement getter and setter methods for accessing private properties
- Add methods for common product operations (check stock, update price, etc.)
- Implement a method to display product information
- Create example usage code to demonstrate the class
Step 3: Implement the Solution
We'll implement the solution according to our plan, creating a Product class with all the necessary components.
Step 4: Review and Refine
After implementation, we'll test the class, verify that it works as expected, and refine it if necessary.
Implementing the Solution
Basic Solution
Let's start with a basic implementation of our Product class:
<?php
/**
* Product class for e-commerce applications
*
* This class represents a product in an online store with
* properties to store product details and methods to
* perform common operations.
*/
class Product {
// Private properties - can only be accessed within this class
private $id;
private $name;
private $price;
private $description;
private $stockQuantity;
// Protected property - can be accessed in this class and child classes
protected $category;
/**
* Constructor - initializes the product
*
* @param int $id Product ID
* @param string $name Product name
* @param float $price Product price
* @param string $description Product description
* @param int $stockQuantity Quantity in stock
* @param string $category Product category
*/
public function __construct($id, $name, $price, $description, $stockQuantity, $category = 'Uncategorized') {
// Initialize properties
$this->id = $id;
$this->name = $name;
$this->price = $price;
$this->description = $description;
$this->stockQuantity = $stockQuantity;
$this->category = $category;
}
/**
* Get product information as an array
*
* @return array Product details
*/
public function getInfo() {
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'description' => $this->description,
'stock_quantity' => $this->stockQuantity,
'category' => $this->category,
'in_stock' => $this->isInStock()
];
}
/**
* Update the product price
*
* @param float $newPrice New price
* @return boolean Success status
*/
public function updatePrice($newPrice) {
// Validate the new price
if ($newPrice >= 0) {
$this->price = $newPrice;
return true;
}
return false;
}
/**
* Reduce the stock quantity
*
* @param int $quantity Quantity to reduce by
* @return boolean Success status
*/
public function reduceStock($quantity = 1) {
// Check if we have enough stock
if ($this->stockQuantity >= $quantity) {
$this->stockQuantity -= $quantity;
return true;
}
return false;
}
/**
* Check if the product is in stock
*
* @return boolean In stock status
*/
public function isInStock() {
return $this->stockQuantity > 0;
}
/**
* Calculate discount price
*
* @param float $percentage Discount percentage
* @return float Discounted price
*/
private function calculateDiscount($percentage) {
$discount = $this->price * ($percentage / 100);
return $this->price - $discount;
}
/**
* Apply a discount to the product
*
* @param float $percentage Discount percentage
* @return boolean Success status
*/
public function applyDiscount($percentage) {
if ($percentage > 0 && $percentage <= 100) {
$newPrice = $this->calculateDiscount($percentage);
return $this->updatePrice($newPrice);
}
return false;
}
// Getter methods
/**
* Get product ID
*
* @return int Product ID
*/
public function getId() {
return $this->id;
}
/**
* Get product name
*
* @return string Product name
*/
public function getName() {
return $this->name;
}
/**
* Get product price
*
* @return float Product price
*/
public function getPrice() {
return $this->price;
}
/**
* Get product description
*
* @return string Product description
*/
public function getDescription() {
return $this->description;
}
/**
* Get stock quantity
*
* @return int Stock quantity
*/
public function getStockQuantity() {
return $this->stockQuantity;
}
/**
* Get product category
*
* @return string Product category
*/
public function getCategory() {
return $this->category;
}
// Setter methods
/**
* Set product name
*
* @param string $name New product name
*/
public function setName($name) {
$this->name = $name;
}
/**
* Set product description
*
* @param string $description New product description
*/
public function setDescription($description) {
$this->description = $description;
}
/**
* Set product category
*
* @param string $category New product category
*/
public function setCategory($category) {
$this->category = $category;
}
}
?>
Example Usage
Now let's see how to use our Product class:
<?php
// Include the Product class
require_once 'class-product.php';
// Create a new product
$laptop = new Product(
1, // ID
'MacBook Pro', // Name
1299.99, // Price
'Powerful laptop for developers', // Description
10, // Stock quantity
'Electronics' // Category
);
// Display product information
echo "<h2>Product Information</h2>";
echo "<pre>";
print_r($laptop->getInfo());
echo "</pre>";
// Check if product is in stock
if ($laptop->isInStock()) {
echo "<p>The {$laptop->getName()} is in stock with {$laptop->getStockQuantity()} units available.</p>";
} else {
echo "<p>The {$laptop->getName()} is out of stock.</p>";
}
// Apply a 10% discount
$laptop->applyDiscount(10);
// Show the new price
echo "<p>After 10% discount, the price is $" . number_format($laptop->getPrice(), 2) . "</p>";
// Simulate a purchase
echo "<p>Processing purchase...</p>";
if ($laptop->reduceStock(2)) {
echo "<p>Purchase successful! Remaining stock: {$laptop->getStockQuantity()}</p>";
} else {
echo "<p>Purchase failed! Not enough stock.</p>";
}
// Update product information
$laptop->setName('MacBook Pro M3');
$laptop->setDescription('Latest model with M3 chip');
// Display updated information
echo "<h2>Updated Product Information</h2>";
echo "<pre>";
print_r($laptop->getInfo());
echo "</pre>";
?>
This example demonstrates:
- Creating a new product object
- Getting product information
- Checking if the product is in stock
- Applying a discount
- Reducing stock (simulating a purchase)
- Updating product information
Advanced Solution: Adding More Features
Now let's enhance our Product class with more advanced features, such as:
- Product validation
- Method chaining
- Static properties and methods
<?php
/**
* Enhanced Product class for e-commerce applications
*
* An advanced implementation with additional features like
* product validation, method chaining, and static methods.
*/
class Product {
// Private properties
private $id;
private $name;
private $price;
private $description;
private $stockQuantity;
protected $category;
// Static properties
private static $totalProducts = 0;
private static $validCategories = [
'Electronics',
'Clothing',
'Books',
'Home',
'Beauty',
'Sports',
'Toys',
'Food',
'Other'
];
/**
* Constructor - initializes the product
*
* @param int $id Product ID
* @param string $name Product name
* @param float $price Product price
* @param string $description Product description
* @param int $stockQuantity Quantity in stock
* @param string $category Product category
*/
public function __construct($id, $name, $price, $description, $stockQuantity, $category = 'Other') {
// Validate inputs
$this->id = intval($id);
$this->name = $this->validateName($name) ? $name : 'Unknown Product';
$this->price = max(0, floatval($price));
$this->description = $description;
$this->stockQuantity = max(0, intval($stockQuantity));
$this->category = $this->validateCategory($category) ? $category : 'Other';
// Increment total products counter
self::$totalProducts++;
}
/**
* Destructor
*/
public function __destruct() {
// Decrement counter when object is destroyed
self::$totalProducts--;
}
/**
* Validate product name
*
* @param string $name Name to validate
* @return boolean Validation result
*/
private function validateName($name) {
return !empty($name) && strlen($name) <= 100;
}
/**
* Validate product category
*
* @param string $category Category to validate
* @return boolean Validation result
*/
private function validateCategory($category) {
return in_array($category, self::$validCategories);
}
/**
* Get total number of product objects
*
* @return int Total products
*/
public static function getTotalProducts() {
return self::$totalProducts;
}
/**
* Get valid categories
*
* @return array Valid categories
*/
public static function getValidCategories() {
return self::$validCategories;
}
/**
* Add a new valid category
*
* @param string $category New category
* @return boolean Success status
*/
public static function addValidCategory($category) {
if (!empty($category) && !in_array($category, self::$validCategories)) {
self::$validCategories[] = $category;
return true;
}
return false;
}
/**
* Get product information as an array
*
* @return array Product details
*/
public function getInfo() {
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'description' => $this->description,
'stock_quantity' => $this->stockQuantity,
'category' => $this->category,
'in_stock' => $this->isInStock()
];
}
/**
* Update the product price with method chaining
*
* @param float $newPrice New price
* @return Product The product object for chaining
*/
public function updatePrice($newPrice) {
// Validate the new price
if ($newPrice >= 0) {
$this->price = $newPrice;
}
return $this; // Return $this for method chaining
}
/**
* Reduce the stock quantity with method chaining
*
* @param int $quantity Quantity to reduce by
* @return Product The product object for chaining
*/
public function reduceStock($quantity = 1) {
// Check if we have enough stock
if ($this->stockQuantity >= $quantity) {
$this->stockQuantity -= $quantity;
}
return $this; // Return $this for method chaining
}
/**
* Increase the stock quantity with method chaining
*
* @param int $quantity Quantity to increase by
* @return Product The product object for chaining
*/
public function increaseStock($quantity = 1) {
if ($quantity > 0) {
$this->stockQuantity += $quantity;
}
return $this; // Return $this for method chaining
}
/**
* Check if the product is in stock
*
* @return boolean In stock status
*/
public function isInStock() {
return $this->stockQuantity > 0;
}
/**
* Check if the product is low on stock
*
* @param int $threshold Low stock threshold
* @return boolean Low stock status
*/
public function isLowStock($threshold = 5) {
return $this->isInStock() && $this->stockQuantity <= $threshold;
}
/**
* Calculate discount price
*
* @param float $percentage Discount percentage
* @return float Discounted price
*/
private function calculateDiscount($percentage) {
$discount = $this->price * ($percentage / 100);
return round($this->price - $discount, 2);
}
/**
* Apply a discount to the product with method chaining
*
* @param float $percentage Discount percentage
* @return Product The product object for chaining
*/
public function applyDiscount($percentage) {
if ($percentage > 0 && $percentage <= 100) {
$newPrice = $this->calculateDiscount($percentage);
$this->price = $newPrice;
}
return $this; // Return $this for method chaining
}
/**
* Format price with currency symbol
*
* @param string $currencySymbol Currency symbol
* @return string Formatted price
*/
public function getFormattedPrice($currencySymbol = '$') {
return $currencySymbol . number_format($this->price, 2);
}
/**
* Create a product from an array of data
*
* @param array $data Product data
* @return Product New product object
*/
public static function createFromArray($data) {
return new self(
$data['id'] ?? 0,
$data['name'] ?? 'Unknown Product',
$data['price'] ?? 0,
$data['description'] ?? '',
$data['stock_quantity'] ?? 0,
$data['category'] ?? 'Other'
);
}
// Getter and setter methods with method chaining
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($name) {
if ($this->validateName($name)) {
$this->name = $name;
}
return $this;
}
public function getPrice() {
return $this->price;
}
public function getDescription() {
return $this->description;
}
public function setDescription($description) {
$this->description = $description;
return $this;
}
public function getStockQuantity() {
return $this->stockQuantity;
}
public function setStockQuantity($quantity) {
$this->stockQuantity = max(0, intval($quantity));
return $this;
}
public function getCategory() {
return $this->category;
}
public function setCategory($category) {
if ($this->validateCategory($category)) {
$this->category = $category;
}
return $this;
}
}
?>
Advanced Usage Example
Here's how to use the enhanced Product class with its advanced features:
<?php
// Include the enhanced Product class
require_once 'class-product-advanced.php';
// Display valid categories
echo "<h2>Valid Product Categories</h2>";
echo "<ul>";
foreach (Product::getValidCategories() as $category) {
echo "<li>{$category}</li>";
}
echo "</ul>";
// Add a new category
Product::addValidCategory('Automotive');
echo "<p>Added new category: Automotive</p>";
// Create a product from array data
$productData = [
'id' => 1,
'name' => 'MacBook Pro',
'price' => 1299.99,
'description' => 'Powerful laptop for developers',
'stock_quantity' => 10,
'category' => 'Electronics'
];
$laptop = Product::createFromArray($productData);
// Display current product count
echo "<p>Total products: " . Product::getTotalProducts() . "</p>";
// Create another product
$phone = new Product(
2,
'iPhone 14',
999.99,
'Latest smartphone with advanced features',
15,
'Electronics'
);
// Display updated product count
echo "<p>Total products after creating iPhone: " . Product::getTotalProducts() . "</p>";
// Using method chaining
$laptop->setName('MacBook Pro M3')
->setDescription('Latest model with M3 chip')
->updatePrice(1499.99)
->increaseStock(5);
// Display product information
echo "<h2>Product Information (After Chaining)</h2>";
echo "<pre>";
print_r($laptop->getInfo());
echo "</pre>";
// Format price
echo "<p>Formatted price: " . $laptop->getFormattedPrice() . "</p>";
// Check low stock
if ($laptop->isLowStock(20)) {
echo "<p><strong>Warning:</strong> This product is running low on stock!</p>";
}
// Simulate multiple purchases with chaining
echo "<p>Processing multiple purchases...</p>";
$laptop->reduceStock(2)->applyDiscount(5);
echo "<p>After purchases and 5% discount:</p>";
echo "<p>New price: " . $laptop->getFormattedPrice() . "</p>";
echo "<p>Remaining stock: " . $laptop->getStockQuantity() . "</p>";
// Remove a product and check count
unset($phone);
echo "<p>Total products after removing iPhone: " . Product::getTotalProducts() . "</p>";
?>
This advanced example demonstrates:
- Using static properties and methods
- Creating products from array data
- Method chaining for a more fluent interface
- Input validation
- Price formatting
- Stock status checking
Step by Step Instructions
Setting Up the Project
- Create a new folder for your project, for example
product-class - Inside this folder, create a file named
class-product.phpfor the basic implementation - If you want to try the advanced version, create a file named
class-product-advanced.php - Create example usage files:
example-basic.phpandexample-advanced.php
Implementing the Basic Class
- Open
class-product.phpin your code editor - Define the
Productclass with private properties for product details - Create a constructor to initialize these properties
- Implement basic methods like
getInfo(),updatePrice(), etc. - Add getter and setter methods for accessing private properties
- Save the file
Testing the Basic Implementation
- Open
example-basic.phpin your code editor - Include the
class-product.phpfile usingrequire_once - Create a new product object with sample data
- Test each method to ensure it works correctly
- Save the file
- Run the example in your browser or command line to see the results
Extending to the Advanced Version
- Open
class-product-advanced.phpin your code editor - Copy the basic implementation and enhance it with:
- Static properties and methods for tracking all products
- Input validation in the constructor and setter methods
- Method chaining by returning
$thisfrom setter methods - Additional helper methods for formatting and validation
- Save the file
Testing the Advanced Implementation
- Open
example-advanced.phpin your code editor - Include the
class-product-advanced.phpfile - Test the advanced features, including static methods and method chaining
- Save the file
- Run the example to see the enhanced functionality
Code Explanations and Concepts
Properties and Access Modifiers
In our Product class, we used different access modifiers:
- Private properties (
$id,$name, etc.): These can only be accessed within the class itself. This provides encapsulation, preventing direct manipulation from outside the class. - Protected properties (
$category): These can be accessed within the class and any child classes that extend it. This is useful when creating inheritance hierarchies. - Public methods (
getInfo(),updatePrice(), etc.): These form the class's public API, allowing external code to interact with objects.
Access Modifiers Analogy
Think of a class like a house:
- Private members are like your bedroom - only you (the class itself) can access them
- Protected members are like the family room - only family members (the class and its descendants) can access them
- Public members are like your front yard - anyone passing by can see and interact with them
Constructor and Initialization
The constructor __construct() is a special method that runs automatically when an object is created. In our class, it:
- Takes parameters for all necessary product information
- Initializes the object's properties with these values
- In the advanced version, it also validates inputs and increments a counter
Getters and Setters
Since our properties are private, we provide public methods to access them:
- Getter methods (
getName(),getPrice(), etc.) allow reading property values - Setter methods (
setName(),setDescription(), etc.) allow changing property values
This pattern gives us control over how properties are accessed and modified, allowing for validation and other logic.
Method Chaining
In the advanced version, we implemented method chaining by returning $this from methods that modify the object:
public function setName($name) {
if ($this->validateName($name)) {
$this->name = $name;
}
return $this; // Return $this for method chaining
}
This allows for more concise and readable code:
$product->setName('New Name')
->setDescription('New description')
->updatePrice(99.99);
Static Properties and Methods
Static members belong to the class itself, not to individual objects:
- Static properties (like
$totalProducts) store information shared by all instances - Static methods (like
getTotalProducts()) provide functionality that doesn't depend on individual object state
We access these using the self:: keyword within the class and ClassName:: from outside.
Real-World Applications
WordPress Integration
Our Product class could easily be integrated into a WordPress plugin for an e-commerce site:
<?php
/**
* Plugin Name: Simple Product Manager
* Description: A lightweight product management system
* Version: 1.0.0
* Author: Your Name
*/
// Include the Product class
require_once plugin_dir_path(__FILE__) . 'includes/class-product.php';
/**
* Register custom post type for products
*/
function spm_register_post_types() {
register_post_type('spm_product', [
'labels' => [
'name' => 'Products',
'singular_name' => 'Product'
],
'public' => true,
'has_archive' => true,
'menu_icon' => 'dashicons-cart',
'supports' => ['title', 'editor', 'thumbnail', 'custom-fields']
]);
}
add_action('init', 'spm_register_post_types');
/**
* Convert WordPress post to Product object
*
* @param int $post_id Post ID
* @return Product Product object
*/
function spm_get_product($post_id) {
$post = get_post($post_id);
if (!$post || $post->post_type !== 'spm_product') {
return null;
}
// Get product meta data
$price = get_post_meta($post_id, '_product_price', true);
$stock = get_post_meta($post_id, '_product_stock', true);
$category = get_post_meta($post_id, '_product_category', true);
// Create a Product object
return new Product(
$post_id,
$post->post_title,
floatval($price),
$post->post_content,
intval($stock),
$category
);
}
/**
* Save product data when post is saved
*
* @param int $post_id Post ID
*/
function spm_save_product_data($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
if (isset($_POST['_product_price'])) {
update_post_meta($post_id, '_product_price', sanitize_text_field($_POST['_product_price']));
}
if (isset($_POST['_product_stock'])) {
update_post_meta($post_id, '_product_stock', intval($_POST['_product_stock']));
}
if (isset($_POST['_product_category'])) {
update_post_meta($post_id, '_product_category', sanitize_text_field($_POST['_product_category']));
}
}
add_action('save_post_spm_product', 'spm_save_product_data');
/**
* Display product information on the product single page
*
* @param string $content Post content
* @return string Modified content
*/
function spm_display_product_info($content) {
if (!is_singular('spm_product')) {
return $content;
}
$product = spm_get_product(get_the_ID());
if (!$product) {
return $content;
}
$output = $content;
$output .= '<div class="product-info">';
$output .= '<p class="product-price">Price: $' . number_format($product->getPrice(), 2) . '</p>';
if ($product->isInStock()) {
$output .= '<p class="in-stock">In Stock: ' . $product->getStockQuantity() . ' available</p>';
} else {
$output .= '<p class="out-of-stock">Out of Stock</p>';
}
$output .= '<p class="product-category">Category: ' . $product->getCategory() . '</p>';
$output .= '</div>';
return $output;
}
add_filter('the_content', 'spm_display_product_info');
?>
This example shows how the Product class could be used in a WordPress plugin to:
- Create a custom post type for products
- Convert WordPress posts to
Productobjects - Save product data as post meta
- Display product information on the front-end
Other Real-World Applications
The concepts demonstrated in this class can be applied to many other scenarios:
- User Management: Create a
Userclass to handle user accounts - Content Management: Create classes for different types of content
- Payment Processing: Create classes to handle payment methods and transactions
- Data Validation: Create utility classes for validating different types of data
Further Learning
Next Steps
To build on what you've learned, consider exploring these topics:
- Inheritance: Create child classes like
PhysicalProductandDigitalProductthat extend theProductclass - Interfaces: Define interfaces that classes can implement, such as
ShippableorDownloadable - Namespaces: Organize your classes in namespaces to avoid naming conflicts
- Autoloading: Set up autoloading so you don't need to manually include class files
- Design Patterns: Learn common OOP design patterns like Singleton, Factory, and Observer
Resources
Conclusion
Congratulations! You've successfully created a Product class with properties and methods, and explored how it can be used in real-world applications. Let's recap what we've learned:
- How to define a class with properties and methods
- How to use access modifiers (private, protected, public) for encapsulation
- How to create a constructor to initialize objects
- How to implement getter and setter methods
- How to add functionality through instance methods
- How to use static properties and methods for class-level features
- How to enable method chaining for a more fluent interface
These object-oriented programming concepts are fundamental to building maintainable, reusable, and organized code, especially in larger applications like WordPress plugins and themes.