2015-12-25 2 views
0

Я ищу способ иметь один базовый класс, который может быть расширен несколькими дочерними классами, только один из которых будет активен одновременно. Очень простой пример:PHP/OOP - использование родительского класса для запуска дочерних функций

class API_Base { 

    public $context; 

    public function __construct() { 
     $this->init() 
    } 
} 

class Mailchimp_API extends API_Base { 

    public function init() { 
     $this->context = 'mailchimp'; 
     $this->enabled = false; 
    } 

    public function add_contact($email_address) { 
     // mailchimp API for adding contact 
    } 
} 

class Infusionsoft_API extends API_Base { 

    public function init() { 
     $this->context = 'infusionsoft'; 
     $this->enabled = true; 
    } 

    public function add_contact($email_address) { 
     // infusionsoft API for adding contact 
    } 

} 

Каждый ребенок инициализируется и регистрируется как опция для выбора пользователем. После того, как пользователь выбрал, какую интеграцию использовать, он сохраняется в базе данных. Я хотел бы в будущем доступ к API_Base, чтобы выглядеть примерно так:

$api = new API_Base(); 
$api->context; // should be "infusionsoft" 
$api->add_contact($email_address); 

Так что, когда $api->add_contact() запускается, он работает только add_contact() функции для активной интеграции API.

В конце концов я хотел бы как-то использовать get_class_methods();, чтобы вернуть возможности только активного API, поэтому функции доступа к API могут знать, что возможно (например, списки поддержки электронной почты API, в то время как другие нет, или поддерживают создание пользовательских поля и т. д.).

У меня был некоторый успех при вызове parent::set_context($context); из включенного класса, но я до сих пор не могу понять, как заставить родителя выполнять только методы в «включенном» дочернем классе.

+0

Похоже, вы хотите какой-то завод. Если вы используете фреймворк, например, Symfony, также немного прочитал о контейнере обслуживания. –

ответ

2

Это не то, как наследование работает. Подклассы дочерних объектов наследуются от их родительского класса.

Чтобы решить вашу проблему, вы можете добавить фабричный метод для API_Base, который будет создавать реализацию API по его типу:

class API_Base { 
    public static function createByType($type) 
    { 
     switch ($type) { 
      case 'mailchimp': return new Mailchimp_API(); 
      case 'infusionsoft': return new Infusionsoft_API(); 
      default: throw new \InvalidArgumentException(spintf('Invalid API type "%s"', $type)); 
     } 
    } 
    // other methods 
} 

и использовать его как это:

$api = API_Base::createByType($user->selectedApi); 
$api->context; // should be "infusionsoft" 
$api->add_contact($email_address); 
+0

Это имеет большой смысл. Единственная проблема заключается в том, что мне нужны все классы API, доступные во время выполнения, чтобы они могли представлять себя как доступные параметры для пользователя. Это расширяемая платформа, в которой другие разработчики будут способствовать интеграции, поэтому я не смогу жестко кодировать интеграцию в класс API_Base. Я использую автозагрузчик прямо сейчас, чтобы импортировать все API. Думаю, я мог бы сделать это, имея два класса: один для конфигурации и один для самого API, но я хотел сохранить его простым в одном классе, если это возможно. Есть идеи? –

+0

Вы можете сохранить имена классов для выбранных API и сделать что-то вроде этого: '' '$ className = $ user-> selectedApi; $ api = new $ className(); '' ' –

+0

Спасибо. Вы очень помогли. Добавлено мое окончательное решение внизу для будущих Googlers. –

0

Вы можете рассмотреть реализацию абстрактного класса. Абстрактный класс работает как тот, кто когда-либо расширяет абстрактный класс, может выполнять методы, которые он имеет.

abstract class Something{ 
     function __construct(){ 
     // some stuff 
     } 

     function my_func(){ 
     $this->myTest ; 
     } 

     abstract function my_func(); 

     } 

    class Some extends Something{ 

     function __construct(){ 
     parent::__construct() ; 
     } 

     function my_test(){ 
      echo "Voila" ; 
     } 

    } 
+0

Но как это будет работать, когда у меня будет несколько детей, скажем, Some1 и Some2, каждый из которых имеет функцию 'my_test()? Я хочу, чтобы 'my_test()' выполнялся в «включенном» дочернем элементе. –

0

Я получил это работает в по моему мнению, для меня отлично работает. Вот что я в итоге сделал:

В главном файле плагина есть фильтруемая функция, где другие разработчики могут добавлять новые интеграции, если они им нужны. Первый параметр - это slug (для моего автозагрузчика), а второй - имя класса.

public function get_apis() { 

    return apply_filters('custom_apis', array(
     'infusionsoft-isdk'   => 'MYPLUGIN_Infusionsoft_iSDK', 
     'infusionsoft-oauth'  => 'MYPLUGIN_Infusionsoft_oAuth', 
     'activecampaign'   => 'MYPLUGIN_ActiveCampaign' 
    )); 

} 

Каждая интеграция содержит слизь и имя класса. Тогда в моем классе API_Base у меня есть это в конструкторе:

class API_Base { 

    public $available_apis = array(); 

    public $api; 

    public function __construct() { 

     $configured_apis = main_plugin()->get_apis(); 

     foreach($configured_apis as $slug => $classname) { 

      if(class_exists($classname)) { 

       $api = new $classname(); 
       $api->init(); 

       if($api->active == true) 
        $this->api = $api; 

       $this->available_apis[$slug] = array('name' => $api->name); 

       if(isset($api->menu_name)) { 
        $this->available_apis[$slug]['menu_name'] = $api->menu_name; 
       } else { 
        $this->available_apis[$slug]['menu_name'] = $api->name; 
       } 

      } 

     } 

    } 
} 

И в моем главном файле, ведь включает в себя, я бегу:

self::$instance->api_base  = new API_Base(); 
self::$instance->api   = self::$instance->api_base->api; 

Теперь я могу назвать self::$instance->api->add_contact($email); и она будет срабатывать в зависимости от того - текущий активный API.

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

Смежные вопросы