Object
物件
2019/10/16 (增補內容)
基本概念
這部分跟java滿像的 (詳參: Object Oriented Programming in PHP 、 PHP - What is OOP?)
只是java用「.」而php用「->」。因為「.」已經用在字串相加了。就如同在java裡一樣,可以定義這些變數(屬性)為private,當定義為private時,就不能直接去存取這些變數,只能透過function(java裡的method)存取這些變數(詳參: PHP OOP - Access Modifiers),這些function通常被稱為getter及setter。這樣的作法就稱為封裝(Encapsulation)。方法中如果要取得這些變數,跟java一樣,就需要用this。
在php裡,建構式的宣告是用__construct,注意,是兩個底線。(詳參: PHP - The __construct Function)
function __construct($name, $city, $rank){
$this->name = $name;
$this->city = $city;
$this->rank = $rank;
}
customer.php (詳參: PHP OOP - Classes and Objects)
<?php
class Customer {
/* Member variables */
/* make them private for encapsulation */
private $name;
private $city;
private $rank;
/* constructor */
function __construct($name, $city, $rank){
$this->name = $name;
$this->city = $city;
$this->rank = $rank;
}
/* Member functions */
function setName($name){
$this->name = $name;
}
function getName(){
return $this->name;
}
function setCity($city){
$this->city = $city;
}
function getCity(){
return $this->city;
}
function setRank($rank){
$this->rank = $rank;
}
function getRank(){
return $this->rank;
}
}
?>
物件陣列
在php裡,定義一個陣列
$customerList = array();
也可以在定義的時候就先定義陣列裡的內容:
$customerList = array(
new Customer("Ben","Taipei",3),
new Customer("Mary","Taipei",2),
new Customer("Tom","Tainan",1)
);
新增內容到陣列裡:
array_push($customerList, new Customer("Rich","Yilan",2));
列出所有陣列裡的內容:
foreach( $customerList as $customer ) {
echo "Name is ".$customer->getName()." <br />";
echo "City is ".$customer->getCity()." <br />";
echo "Rank is ".$customer->getRank()." <br />";
}
產生一個Customer的陣列,並印出陣列內容的完整程式。
php裡有個特別的概念,include/require,其實就等於是把被include/require放到這個程式裡,這樣可以重複利用某一個部分的程式,例如,customer.php。
customerApp.php
<?php
require 'customer.php';
$customerList = array(
new Customer("Ben","Taipei",3),
new Customer("Mary","Taipei",2),
new Customer("Tom","Tainan",1)
);
array_push($customerList, new Customer("Rich","Yilan",2));
foreach( $customerList as $customer ) {
echo "Name is ".$customer->getName()." <br />";
echo "City is ".$customer->getCity()." <br />";
echo "Rank is ".$customer->getRank()." <br />";
}
?>
Magic Methods
可以利用php的magic methods,這樣就可以保有encapsulation,又可以不必寫一堆setter跟getter。(詳參: What are PHP Magic Methods? 、16 Magic Methods That PHP Developers Must Know)
customer.php
<?php
class Customer {
/* Member variables */
/* make them private for encapsulation */
private $name;
private $city;
private $rank;
/* constructor */
function __construct($name, $city, $rank){
$this->name = $name;
$this->city = $city;
$this->rank = $rank;
}
//https://culttt.com/2014/04/16/php-magic-methods/
//https://www.tutorialdocs.com/article/16-php-magic-methods.html
//only private variables were set
function __set($variable, $value){}
function __get($variable){
return $this->$variable;
}
}
?>
使用的時候,因為已經沒有定義getter及setter,所以,就直接存取private變數
customerApp.php
<?php
require 'customer.php';
$customerList = array(
new Customer("Ben","Tainan",3),
new Customer("Tom","Taipei",2),
new Customer("Mary","Tainan",1)
);
array_push($customerList, new Customer("Rich","Yilan",2));
foreach( $customerList as $customer ) {
echo "Name is ".$customer->name." <br />";
echo "City is ".$customer->city." <br />";
echo "Rank is ".$customer->rank." <br />";}
?>
直接存取時,不就違反encapsulation了嗎? 跟直接讓別人存取這些變數不同的是,當我們要控制變數的內容時,可以利用__set及__get來控制,這樣的話,即使可以直接存取變數,也都會被迫透過__set及__get來存取這些變數,完全達到encapsulation的目的。例如,我們要控制rank一定要大於等於0。
<?php
class Customer {
/* Member variables */
/* make them private for encapsulation */
private $name;
private $city;
private $rank;
/* constructor */
function __construct($name, $city, $rank){
$this->name = $name;
$this->city = $city;
$this->setRank($rank);
}
//https://culttt.com/2014/04/16/php-magic-methods/
//https://www.tutorialdocs.com/article/16-php-magic-methods.html
//only private variables were set
function __set($variable, $value){
if ($variable == "rank") {
$this->setRank($value);
} else {
$this->$variable = $value;
}
}
function __get($variable){
return $this->$variable;
}
function setRank($value){
if ($value < 0 ) {
$this->rank = 0;
} else {
$this->rank = $value;
}
}
}
?>
排序
如果我們直接對一個物件陣列排序,php會以第一個欄位來作為排序的依據: (PHP sort() Function)
<?php
function printAll($customerList){
foreach( $customerList as $customer ) {
echo "Name is ".$customer->name." <br />";
echo "City is ".$customer->city." <br />";
echo "Rank is ".$customer->rank." <br />";}
}
require 'customer.php';
$customerList = array(
new Customer("Ben","Tainan",3),
new Customer("Tom","Taipei",2),
new Customer("Mary","Tainan",1)
);
array_push($customerList, new Customer("Rich","Yilan",2));
printAll($customerList);
echo "------";
sort($customerList);
printAll($customerList);
echo "------";
?>
結果是:
Name is Ben
City is Tainan
Rank is 3
Name is Tom
City is Taipei
Rank is 2
Name is Mary
City is Tainan
Rank is 1
Name is Rich
City is Yilan
Rank is 2
Name is Ben
City is Tainan
Rank is 3
Name is Mary
City is Tainan
Rank is 1
Name is Rich
City is Yilan
Rank is 2
Name is Tom
City is Taipei
Rank is 2
如果要以其他規則來排序,就必須使用usort (PHP usort() Function / Sort array of objects by object fields in PHP),usort的第二個參數是排序依據的function名稱 (byCity)。
usort($customerList, "byCity");
排序的依據就是回傳一個判斷大小的依據
function byCity($object1, $object2){
return $object1->city > $object2->city;
}
也可以是比較複雜的比較,例如,如果city的內容相同就以name大小排序,如果city的內容不相同就以city大小排序:
function byCity($object1, $object2){
if ($object1->city == $object2->city){
return $object1->name > $object2->name;
}
else {
return $object1->city > $object2->city;
}
}
完整的程式:
<?php
function printAll($customerList){
foreach( $customerList as $customer ) {
echo "Name is ".$customer->name." <br />";
echo "City is ".$customer->city." <br />";
echo "Rank is ".$customer->rank." <br />";}
}
function byCity($object1, $object2){
if ($object1->city == $object2->city){
return $object1->name > $object2->name;
}
else {
return $object1->city > $object2->city;
}
}
require 'customer.php';
$customerList = array(
new Customer("Mary","Tainan",3),
new Customer("Tom","Taipei",2),
new Customer("Ben","Tainan",1)
);
array_push($customerList, new Customer("Rich","Yilan",2));
printAll($customerList);
echo "<br/>";
sort($customerList);
printAll($customerList);
echo "<br/>";
usort($customerList, "byCity");
printAll($customerList);
echo "<br/>";
?>
結果如下:
Name is Mary
City is Tainan
Rank is 3
Name is Tom
City is Taipei
Rank is 2
Name is Ben
City is Tainan
Rank is 1
Name is Rich
City is Yilan
Rank is 2
Name is Ben
City is Tainan
Rank is 1
Name is Mary
City is Tainan
Rank is 3
Name is Rich
City is Yilan
Rank is 2
Name is Tom
City is Taipei
Rank is 2
Name is Ben
City is Tainan
Rank is 1
Name is Mary
City is Tainan
Rank is 3
Name is Tom
City is Taipei
Rank is 2
Name is Rich
City is Yilan
Rank is 2
簡易購物車
product.php
<?php
class Product {
/* Member variables */
/* make them private for encapsulation */
private $id;
private $name;
private $price;
/* constructor */
function __construct($id, $name, $price){
$this->id = $id;
$this->name = $name;
$this->setPrice($price);
}
//https://culttt.com/2014/04/16/php-magic-methods/
//https://www.tutorialdocs.com/article/16-php-magic-methods.html
//only private variables were set
function __set($variable, $value){
if ($variable == "price") {
$this->setPrice($value);
} else {
$this->$variable = $value;
}
}
function __get($variable){
return $this->$variable;
}
function setPrice($value){
if ($value < 0 ) {
$this->price = 0;
} else {
$this->price = $value;
}
}
}
?>
productList.php
使用ProductData儲存產品資料,點選產品可以看到產品的內容並輸入購買數量 (productShow.php)。
<?php
require 'productData.php';
$productData = new ProductData();
$productList = $productData->listAllProducts();
?>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<table>
<tr>
<th>產品編號</th>
<th>產品名稱</th>
<th>價格</th>
<th></th>
</tr>
<?php
foreach( $productList as $product ) {
?>
<tr>
<td><?=$product->id?></td>
<td><?=$product->name?></td>
<td><?=$product->price?></td>
<td><a href="productShow.php?id=<?=$product->id?>">看內容</a></td>
</tr>
<?php
}
?>
</table>
</body>
</html>
productData.php
將產品資料儲存在陣列中,未來資料儲存在資料庫中。
<?php
require 'product.php';
class ProductData{
/* Member variables */
/* make them private for encapsulation */
private $products;
/* constructor */
function __construct(){
$this->products= array(
new Product("0","iPhone 11",24900),
new Product("1","iPhone 11 Pro",35900),
new Product("2","iPhone 11 Pro Max",39900),
new Product("3","iPhone XR",21500),
new Product("4","iPhone 8",15900),
new Product("5","iPhone 8 Plus",19900)
);
}
function listAllProducts(){
return $this->products;
}
function retrieveProduct($id){
return $this->products[$id];
}
}
?>
productShow.php
利用ProductData取得產品資料,輸入數量後呼叫cardAdd.php,將資料放進session裡。
<?php
require 'productData.php';
$id = $_GET["id"] ?? "0";
$productData = new ProductData();
$product = $productData->retrieveProduct($id);
?>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<form action="cartAdd.php" method="post">
產品編號: <?=$product->id?><br>
<input type="hidden" name="id" value="<?=$product->id?>">
產品名稱: <?=$product->name?><br>
價格: <?=$product->price?><br>
數量: <input type="text" name="qty"><br>
<input type="submit" value="Add">
</form>
</body>
</html>
cart.php
<?php
class Cart {
/* Member variables */
/* make them private for encapsulation */
//these will be stored in session
private $id;
private $qty; //quantity
//for cartList only
private $product;
/* constructor */
function __construct($id, $qty, $product=null){
$this->id = $id;
$this->setQty($qty);
$this->product = $product;
}
//https://culttt.com/2014/04/16/php-magic-methods/
//https://www.tutorialdocs.com/article/16-php-magic-methods.html
//only private variables were set
function __set($variable, $value){
if ($variable == "qty") {
$this->setQty($value);
} else {
$this->$variable = $value;
}
}
function __get($variable){
return $this->$variable;
}
function setQty($value){
if ($value < 1 ) {
$this->qty = 1;
} else {
$this->qty = $value;
}
}
}
?>
cardAdd.php,將資料放進session後,呼叫cartList。
<?php
require 'cart.php';
session_start();
$id = $_POST["id"] ?? "0";
$qty = $_POST["qty"] ?? "1";
//initialize session for first item
if (!isset($_SESSION["cart"])){
$_SESSION["cart"]=[];
}
array_push($_SESSION["cart"], new Cart($id,$qty));
header("Location: cartList.php");
?>
cartList.php,顯示的時候,利用ProductData取得產品資料,cartClear.php會清空購物車。
<?php
require 'cart.php';
require 'productData.php';
$productData = new ProductData();
session_start();
//prevent error when session does not exist
$shoppingCart = $_SESSION["cart"] ?? [];
//initialize an empty list
$shoppingList = [];
foreach( $shoppingCart as $item ) {
$product = $productData->retrieveProduct($item->id);
//combine cart with product
$fullProduct = new Cart($item->id, $item->qty, $product);
array_push($shoppingList, $fullProduct);
}
?>
<html>
<head>
<meta charset="utf-8">
</head>
<a href="cartClear.php">清除購物車</a><br>
<a href="productList.php">繼續購物</a><br>
<body>
<table>
<tr>
<th>產品編號</th>
<th>產品名稱</th>
<th>價格</th>
<th>數量</th>
</tr>
<?php
foreach( $shoppingList as $item ) {
?>
<tr>
<td><?=$item->id?></td>
<td><?=$item->product->name?></td>
<td><?=$item->product->price?></td>
<td><?=$item->qty?></td>
</tr>
<?php
}
?>
</table>
</body>
</html>
cartClear.php 清空購物車,回到cartList。
<?php
session_start();
unset($_SESSION["cart"]);
header("Location: cartList.php");
?>
** 作業 **
- 利用產品與購物車的範例,新增登入介面(請利用session作業的登入),沒登入只能看到產品,無法購物,登入後才能將產品置入購物車。
- 登入後,可以選擇讓產品資料依name由小到大排序。(挑戰題)
- 登入後,可以選擇讓產品資料依price由小到大排序。(挑戰題)