2011-02-06 2 views
0

Для простоты предположим, что у меня есть 2 класса User и UserStatus, используемые в веб-приложении.Объект, определяемый как свойство в классе PHP по мере необходимости (ленивая загрузка)

<?php 

// library code: 
class UserStatus { 
    protected $_status = NULL; 

    private function fetchDataFromDB() { 
    // regular DB stuff 
    $this->_status = ... 
    // result will be something like 'online', 'away', etc. 
    } 

    public function getIcon() { 
    global $icon_array; 

    if (is_null($this->_status)) { 
     $this->fetchDataFromDB() 
    } 
    return $icon_array[$this->_status]; 
    } 
} 

class User { 
    protected $user_id; 
    public $user_name; 
    protected $status; 

    public function __construct() {} 

    public static function getAll() { 
    // some DB stuff 
    return $users; 
    } 
} 

// and now, in index.php: 
$users = User::getAll(); 

// echoes the icon to use to reflect the current user status 

foreach ($users as $user) { 
    echo <img src="$user->status->getIcon()"/>; 
} 

?> 

В большинстве запроса HTTP статусный объект не будет использоваться, так что я искал способ, чтобы только создать экземпляр его по мере необходимости (назовем его отложенной загрузки). Как перехватить вызов status->method() и создать этот объект «на лету»?

Важен отметить, что мне нужно $user_id доступные в классе UserStatus, в противном случае метод fetchDataFromDB() не будет знать, какой пользователь он должен получать данные. Как это сделать?

Я смотрел на некоторые интересные вещи по этому вопросу, как Фабьен Potencier-х What is Dependency Injection? и Pimple - a PHP 5.3 dependency injection container, а также некоторые статьи о Proxy Pattern, но реализовать их, похоже, я возиться много с текущим кодом. Есть ли более простой способ?

ответ

3

Может быть, им что-то отсутствует, но это, кажется, самым простым решением в данном случае будет иметь свой поглотитель для статуса просто создать объект, если оно не существует ...

public function getStatus() 
{ 
    if(!isset($this->status)) 
    { 
    // or however you creat this object.. 
    $this->status = new UserStatus($this->user_id); 
    } 

    return $this->status; 
} 

public function __get($property) 
{ 
    $method = 'get'.ucfirst($property); // getStatus 
    if(method_exists($this, $method)) 
    { 
     return $this->$method(); 
    } 
} 

С помощью __get магического метода в любое время вы делаете $user->status, он назовет $user->getStatus(). Конечно, вы также можете просто получить к нему доступ, например: $user->getStatus()->getIcon().

Однако вы решили настроить доступ к своим объектам, и я порекомендовал бы их последовательно в рамках всей вашей модели.

+0

Это действительно способ сделать это, но класс 'UserStatus' предоставляет несколько методов, один из которых является' getIcon() ', как показано выше. Используя ваше предложение, я должен вызвать метод значка в двухэтапной процедуре: '$ user-> getStatus(); $ User-> СТАТУС> getIcon() '. Я пытался сделать его более умным, но эй, он работает. Благодарю. – noisebleed

+0

нет, вы не можете использовать magiv '__get', я предположил, что вы собираетесь это делать, поскольку« статус »был защищен, а не публичный, иначе вы не могли бы получить доступ к нему непосредственно из-за пределов своего класса пользователя ... поэтому я его не включил. .. см. мое обновление. – prodigitalson

+0

Да, идея состояла в том, чтобы использовать '__get()', и я оставил «статус» с целью защиты, но я не кодировал '__get()' просто потому, что не знал наилучшего подхода к тому, как это сделать. Ваше обновление решает мои сомнения, это очень хороший подход, и я рад его принять. Еще раз спасибо.О том, как это сделать последовательным образом, это хороший совет, который я не уважаю прямо сейчас :(Я рассматриваю переписку своей домашней структуры с использованием компонентов Symfony, таких как DependencyInjection и EventDispatcher, но я все еще немного путают о том, как их расставить. – noisebleed

1

Вы можете поместить класс статус в другом файле, а затем использовать автозагрузку mechnism РНР:

http://php.net/manual/de/language.oop5.autoload.php

не загрузить файл до тех пор, пока к нему доступ.

Ходят слухи, что автоматическая загрузка (или фактически любая условная загрузка) является проблемой для кеш-кода и оптимизаторов, хотя, к сожалению, я не слишком много знаю об этом воздействии.

P.S .: В руководстве не говорится об эксплойте rhis в этой точке: вы также можете использовать spl_autoload_register(), а не просто определять магическую функцию __autoload. Это немного более мощно.

+0

У меня есть несколько небольших классов и помещать их в файл, каждый из которых не то, что я хотел бы сделать. Я попытаюсь решить эту проблему с чем-то в строке того, что предлагалось, а затем вернуться сюда, если это не сработает. Благодарю. – noisebleed