Как мне переключить пользовательскую реализацию интерфейса на условие в http controller в Laravel

#laravel #laravel-5

#laravel #laravel-5

Вопрос:

У меня есть метод в моем контроллере, который в основном просто сохраняет форму в базе данных. Моя форма очень большая, и в ней более 30-40 полей. Итак, мне нужно сохранить эту информацию в разных 3 таблицах по условию.

Например :

 foreach($request->all() as $answer):

    if($answer->employeeType === 1){

    //store data to type_one_table

   } else if($answer->employeeType === 2){

     //store data to type_two_table

   } else if($answer->employeeType === 3){

     //store data to type_two_table

  } else {

    //store data to some_other_table

    }

endforeach;
  

Итак, я думал использовать какой-нибудь пользовательский, StoreInterface который будет иметь некоторый store метод.

и извлечь приведенную выше логику в ее собственную реализацию.

И затем в моем конструкторе Controllers получите этот экземпляр интерфейса.

Но как я могу вызвать, что implementation мне нужно для конкретного условия.

Могу ли я сделать это с помощью контроллера?

Или я должен использовать другую стратегию в этом случае.

пожалуйста, направьте меня.

Спасибо.

Ответ №1:

У вас может быть общий интерфейс, подобный этому:

 interface EmployeeStoreContract
{
    public function saveAnswer();
}
  

Затем реализуйте это для всех различных способов сохранения ответа, например:

 class FirstTypeEmployee implements EmployeeStoreContract
{
    public function saveAnswer()
    {
        // do something
    }
}
  

И в вашем контроллере, чтобы выяснить, как сохранить данные запроса:

 public function store()
{
    $employeeTypesMap = [
        1 => 'FirstTypeEmployee',
        2 => 'SecondTypeEmployee',
        3 => 'ThirdTypeEmployee',
    ];

    foreach ($request->all() as $answer) {
        $employeeType = $answer->employeeType;

        if (!array_key_exists($employeeType, $employeeTypesMap)) {
            throw new InvalidArgumentException('Answer type is not available.');
        }

        $employeeStrategyClass = "App\Service\Employee\{$employeeTypesMap[$employeeType]}";
        $employeeStrategyObject = new $employeeStrategyClass;

        $employeeStrategyObject->saveAnswer($answer);
    }
}
  

Пожалуйста, обратите внимание, что пространство имен, в котором у вас есть эти классы AppServiceEmployee , может быть любым, что вы хотите. Также не стесняйтесь создавать абстрактный класс или признак для повторного использования некоторой функциональности каждого класса employee.

Комментарии:

1. Спасибо за ответ. не могли бы вы, пожалуйста, объяснить последние 3 строки? откуда new $strategyClass; взялось? и почему `$employeeStrategyObject` это существует, если мы не используем его здесь.

2. @TrueCode, да, моя ошибка, извините. Итак, по сути, вы создаете экземпляр класса в зависимости от типа ответа и вызываете saveAnswer() объект.

3. я немного новичок в ООП, но у меня возник вопрос в этом конкретном примере, имеет ли смысл иметь EmployeeStoreContract ? Здесь в основном мы просто переключаемся между 3 классами.

4. Конечно, интерфейс EmployeeStoreContract заключается в том, чтобы просто убедиться, что все классы employee, формирующие $employeeTypesMap массив, имеют saveAnswer() метод, так что здесь на самом деле ничего особенного. Вы можете сделать то же самое следующим образом if (method_exists($employeeStrategyObject, 'saveAnswer')) {...} .

5. Спасибо. Понятно. просто интересно, как мы можем сделать это больше INTERFACE WAY и получить выгоду от полиморфизма? Я имею в виду, что в этом случае мы могли бы использовать abstract class , если мы хотим убедиться, что метод должен быть реализован.

Ответ №2:

Может быть много подходов.

Я предложу 2 способа, которые я бы лично использовал:

1) Создайте специальные модели eloquent, по одной для каждого случая.

Например:

 if($answer->employeeType === 1){
     EmployeeOne::create($answer);
}
  

И ваша красноречивая модель может выглядеть следующим образом:

 <?php

namespace AppModels;

use IlluminateDatabaseEloquentModel;
use DB;

class EmployeeOne extends Model
{
    protected $table = 'type_one_table';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        // your db field names
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
    ];

    public $timestamps = false;
}
  

Убедитесь, что ключи массива, которые вы передаете функции create, должны соответствовать полям вашей базы данных. Я только что привел вам пример первого if , чтобы вы могли использовать его здесь.

2) Другим подходом было бы создать модель, которая принимает в качестве параметра имя таблицы. Итак, на основе вашего if вы собираетесь передать имя таблицы, которое хотите вставить, и данные, которые должны быть вставлены.

 if($answer->employeeType === 1){
    $this->myModel->insertData($answer,'type_one_table');
}
  

И в вашей модели у вас будет что-то вроде:

 public function insertData($answer,$table){
    $query = DB::table($table)->insertGetId($answer); // returns the id of the new record

    return $query;
}
  

Итак, в каждом if-else заявлении вы просто меняете имя таблицы, передаваемое вашей функции model, и все.

Комментарии:

1. Спасибо за ответ. Но мои ключи массива более сложны и содержат вложенные данные, которые я буду хранить как json. также необходимо отфильтровать и удалить некоторые входные данные.. Таким образом, логика (код) будет больше… поэтому я не хочу помещать всю логику в свой контроллер. Какой другой подход я могу использовать?