Как удалить определенные поля из всех объектов набора записей в Lithium?

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

Я хочу сделать тот же поисковый запрос, но затем удалить поля ID из набора записей, если будет отображаться файл CSV. Вы заметите, что идентификатор загрузки извлекается, даже если его нет в массиве полей, поэтому простое изменение массива полей на основе типа запроса не сработает.

Я пробовал следующее в действии index моего контроллера загрузки:

<?php
namespace app\controllers;
use app\models\Downloads;

class DownloadsController extends \lithium\action\Controller {

    public function index() {

        // Dynamic conditions
        $conditions = array(...);

        $downloads = Downloads::find('all', array(
            'fields' => array('user_id', 'Surveys.name'),
            'conditions' => $conditions,
            'with' => 'Surveys',
            'order' => array('created' => 'desc')
        ));

        if ($this->request->params['type'] == 'csv') {
            $downloads->each(function ($download) {

                // THIS DOES NOT WORK
                unset($download->id, $download->user_id);

                // I HAVE TRIED THIS HERE AND THE ID FIELDS STILL EXIST
                // var_dump($download->data());
                // exit;

                return $download;
            });
            return $this->render(array('csv' => $downloads->to('array')));
        }

        return compact('downloads');
    }

}
?>

Я думал, что в объекте сущности есть магический метод __unset(), который будет вызываться при вызове стандартной функции PHP unset() в поле сущности.

Было бы здорово, если бы была функция $recordSet->removeField('field'), но не могу найти.

Любая помощь будет принята с благодарностью.


person Jason Royle    schedule 19.06.2013    source источник


Ответы (2)


Возможно, вам следует сделать $downloads = $downloads->to('array');, перебрать массив с помощью цикла for, удалить эти поля из каждой строки, а затем вернуть этот массив. Если вам нужно сделать то же самое для многих действий, вы можете настроить собственный обработчик мультимедиа, который может изменять данные без необходимости логики для этого в вашем контроллере.

Взгляните на этот пример в модульном тесте класса Lithium Media.

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

In config/bootstrap/media.php:

Media::type('csv', 'application/csv', array(
    'encode' => function($data, $handler, $response) {
        $request = $handler['request'];
        $privateKeys = null;
        if ($request->privateKeys) {
            $privateKeys = array_fill_keys($request->privateKeys, true);
        }
        // assuming your csv data is the first key in
        // the template data and the first row keys names
        // can be used as headers
        $data = current($data);
        $row = (array) current($data);
        if ($privateKeys) {
            $row = array_diff_key($row, $privateKeys);
        }
        $headers = array_keys($row);
        ob_start();
        $out = fopen('php://output', 'w');
        fputcsv($out, $headers);
        foreach ($data as $record) {
            if (!is_array($record)) {
                $record = (array) $record;
            }
            if ($privateKeys) {
                $record = array_diff_key($record, $privateKeys);
            }
            fputcsv($out, $record);
        }
        fclose($out);
        return ob_get_clean();
    }
));

Ваш контроллер:

<?php
namespace app\controllers;
use app\models\Downloads;

class DownloadsController extends \lithium\action\Controller {

    public function index() {

        $this->request->privateKeys = array('id', 'user_id');

        // Dynamic conditions
        $conditions = array(...);

        $downloads = Downloads::find('all', array(
            'fields' => array('user_id', 'Surveys.name'),
            'conditions' => $conditions,
            'with' => 'Surveys',
            'order' => array('created' => 'desc')
        ));

        return compact('downloads');
    }

}
?>
person rmarscher    schedule 19.06.2013
comment
Я перечитал ваш вопрос и, очевидно, вы уже знали о пользовательском обработчике мультимедиа. Но в любом случае, я надеюсь, что это даст вам некоторые идеи. - person rmarscher; 20.06.2013
comment
Спасибо за это, я скоро попробую и дам вам знать, как это происходит, но это похоже на более чистое решение. - person Jason Royle; 21.06.2013
comment
Спасибо за это! Только одно, этот код можно сделать еще лучше, используя lithium\util\Set::flatten(), который сворачивает многомерный массив в одно измерение, используя путь к массиву с разделителями для каждого ключа элемента массива, т. е. array(array('Foo' => array('Bar' => 'Far'))) становится array('0.Foo.Bar' => 'Far'). - person leek; 10.10.2013
comment
@leek Я разместил код на Github — gist.github.com/rmarscher/6970845. Где вы собирались использовать flatten? На $data? Разместите ссылку на отредактированную версию, если вы ее сделаете. - person rmarscher; 14.10.2013

Почему бы тогда просто не установить динамически массив $fields?

public function index() {
    $type = $this->request->params['type'];
    //Exclude `user_id` if request type is CSV
    $fields = $type == 'csv' ? array('Surveys.name') : array('user_id', 'Surveys.name');
    $conditions = array(...);
    $with = array('Surveys');
    $order = array('created' => 'desc');

    $downloads = Downloads::find('all', compact('conditions', 'fields', 'with', 'order'));
    //Return different render type if CSV
    return $type == 'csv' ? $this->render(array('csv' => $downloads->data())) : compact('downloads');
}

В этом примере вы можете увидеть, как я отправляю массив для вашего обработчика CSV, иначе в представление попадает объект $downloads RecordSet.

person Dave Golding    schedule 22.06.2013
comment
Это по-прежнему будет получать идентификатор загрузки, и цель состоит в том, чтобы удалить все поля идентификатора, а не только поле идентификатора пользователя. - person Jason Royle; 29.07.2013