2012-09-24 70 views
1

PHP新手& OOP讓我忍無可忍......在寫一篇蹩腳的M $ VBA垃圾的一生之後,我處於設計和編寫我的第一個PHP OOP網站的樂觀早期階段。PHP母公司和子類數據庫操作

我有一個類「用戶」,其中有一個與相關的數據庫調用等保存方法...(實際上我有DB和DBUtils類處理連接和CRUD - 我的業務類只是調用選擇,更新,刪除上DB的Utils方法和傳遞數據的關聯數組)

Anywho ...我的「用戶」類是由之上「用戶」有一些額外的屬性內容十分重要...

的「系統管理員」擴展類

處理「Admin」上保存方法的最佳方法是什麼?我明白,如果我添加一個保存方法的管理類它將取代用戶,但我不希望它。我想在Admin上編寫save方法來處理特定於Admin對象的屬性等等,以及從用戶保存方法中處理的「User」繼承的屬性。

這有道理嗎?它是我正在尋找的特定OOP模式嗎?任何幫助或指導我應該如何設計和構建這個代碼將不勝感激。

編輯:哇!感謝下面的所有答案。不確定哪個是我的首選呢。我將不得不做一些玩耍...

+2

即使您重寫Admin類中的save方法,仍然可以通過執行parent :: save()來調用父類(即User)中的方法。 –

+1

爲什麼不採取現成的ORM?例如,我最喜歡的:[RedBean](http://redbeanphp.com/#/Tutorial)它使用起來非常簡單,並提供了快速開發。 –

回答

1

您正試圖實現Active record pattern

對象持久化的一種方法是提供一個共同的祖先類[例如,BasicEntity]每個子類中延伸,它建立基於給定數據模式的查詢:

class BasicEntity 
{ 

    protected $tablename; 

    protected $schema; 

    public function update() 
    { 

     $fields = ""; 
     $placeholders = ""; 

     foreach($this -> schema as $field => $type) 
     { 

      // you join the fields here to get something like ('username', 'email', 'enabled', 'createdAt', 'password') 
      // then you write your PDO statement providing placeholders like (:?, :?, :?, :?, :?) 
      // you'll have to bind parameters based on their $type [int, string, date] 

     } 

     $query = sprintf(
      "UPDATE %s SET VALUES(%s) = %s", 
      $this -> tablename, 
      $fields, 
      $placeholders 
     ); 

     // execute statement here, handle exceptions, and so... 

    } 

} 

所以你User類將是這樣的:

class User extends BasicEntity 
{ 

    protected $id; 
    protected $username; 
    protected $email; 
    protected $password; 
    protected $enabled; 
    protected $createdAt; 

    public function __construct() 
    { 

     $this -> tablename = '_user'; 
     $this -> schema = array(
      'id'  => 'int', 
      'username' => 'string', 
      'email'  => 'string', 
      'password' => 'string', 
      'enabled' => 'int', 
      'createdAt' => 'datetime' 
     ); 

    } 

} 

而且你Admin類:

class Admin extends User 
{ 

    protected $additionalProperty; 

    public function __construct() 
    { 

     parent::__construct(); 

     $this -> schema['additionalProperty'] = 'string'; 

    } 

} 

調用update()將根據類架構構建正確的查詢。這種方法與工作在低複雜度,因爲你會發現:[!在同一個表]

  • 如果你擴展你的實體,您需要甚至不提供行空表的字段有這樣的班級[在這種情況下,additionalProperty];
  • 如果您的模式更改[例如你改變了一個變量名稱],你必須將它硬編碼到類的構造函數中,這使得它更難以維護;如果你想要處理實體之間的關係,那麼在每個SELECT語句中編寫正確的連接將是一個很大的痛苦,除非你只寫了很多單獨的查詢,從而導致性能下降。

要解決的首先,你需要的對象組成,所以你不要讓你的主表增長多少[它只是獲取一個外部AdditionalPropertyList實體引用,例如。

要解決第二個問題,必須將架構保留在外部文件中或使用inline annotations

要解決第三個問題,您必須編寫自己的ORM [Object Relational Mapping],或者更好地切換到an existing one

無論出於什麼學習的好處,我都會站在巨人的肩膀上,如果你打算構建一個可擴展的和可維護的應用程序,我會選擇一個框架。

3

當你寫你保存在管理類的方法,你可以推遲爲標準保存父對象後執行額外的工作...

class Admin extends User 
{ 
    // Other methods 

    public function save() 
    { 
     parent::save(); 
     // now save your additional fields wherever they need to go 
    } 
} 

在附註上,雖然使用對象來表示域(例如用戶和管理員)是很好的,但它有時可以在其他地方使用持久性邏輯,例如使用The Repository Pattern。這可以防止您的對象被綁定到持久性機制。

2

有很多方法可以解決這個問題。其中之一是有一個受保護的方法,可以在擴展類中重寫以提供該擴展類的數據;

一個例子:

class User 
{ 
    public function Save() 
    { 
    $savestuff = $this->GetProperties(); 
    // do save acctions 
    } 

    protected function GetProperties() 
    { 
    return 'user prop'; 
    } 
} 

class Admin Extends User 
{ 
    protected function GetProperties() 
    { 
    return 'admin prop'; 
    } 
} 
1

儘管是新的面向對象,它聽起來就像你打的PHP的對象模型的主要缺陷之一。你想要做的事情可以通過方法重載輕鬆實現(具有幾個名稱相同的方法,但採用不同的參數,被調用的實際成員函數取決於參數)的能力。

您可以解決這個問題,通過檢查你的save方法PARAMS在Admin類:

class Admin 
{ 
    public function save(Model_Abstract $model = null)//type hinting 
    { 
     if ($model instanceof Model_Admin) 
     { 
      //do admin insert/update stuff here 
      return; 
     } 
     parent::save($model);//call regular method 
    } 
} 
5

您的主要問題,從事實,你已經忽略的OOP核心思想源於一個:single responsibility principle .. 話又說回來了,好像大家誰提供「答案」有沒有想法什麼SRP要麼。

你所說的「業務邏輯」應該與存儲相關的操作分開。 User no Admin的實例都不應該知道存儲執行的方式,因爲它不是業務邏輯。

$entity = new Admin; 
$mapper = new AdminMapper($db); 

$entity->setName('Wintermute'); 
$mapper->fetch($entity); 
//retrieve data for admin with that name 

$entity->setName('Neuromancer'); 
$mapper->store($entity); 
// rename and save 

你上面看到的是一個極其簡化的​​模式的應用。這種模式的目標是分離商業和存儲邏輯。

如果,而不是直接實例映射器$mapper = new AdminMapper($db),它會通過一個injected工廠$mapper = $this->mapperFactory->build('Admin')(因爲它應該是在適當的代碼庫)來提供,你有沒有對存儲介質指示。數據可以存儲在SQL DB或文件中,也可以存儲在某個遠程REST API中。如果mapper的界面保持不變,您可以隨時替換它。

工廠的使用可以避免與特定類名的緊密耦合,並且在SQL數據庫映射器的情況下,讓您在每個實例相同的數據庫連接中注入數據。

要了解更多信息,您可以閱讀this,thisthis

但是,如果您正在認真研究OOP,那麼的讀數this book爲必填項

+0

「然後,似乎每個提供」答案「的人都不知道SRP是什麼」 - 公平地說,我相信我已經介紹了這一點。此外,存儲庫模式與活動對象模式之間的選擇並不意味着某些東西不是面向對象的 - 因爲我們不知道這些類中是否存在除持久性之外的任何東西,所以它可能不會違反SRP。 – Fenton

+1

@索尼,這就是爲什麼你得到upvote'= P' –

+0

在這種情況下,我接受我過敏! – Fenton