Экспорт CSV в контроллер laravel 5

Итак, я сделал небольшой запрос ajax на мой [email protected].

Теперь, когда я console.log() данные в моем методе успеха, ответ ajax показывает правильные данные. Однако мой CSV не скачал. Итак, у меня есть вся необходимая информация и вы создали csv по существу.

Я думаю, что это возможно связано с настройкой заголовков, возможно?

public function export()
{
    header("Content-type: text/csv");
    header("Content-Disposition: attachment; filename=file.csv");
    header("Pragma: no-cache");
    header("Expires: 0");

    $reviews = Reviews::getReviewExport($this->hw->healthwatchID)->get();
    $columns = array('ReviewID', 'Provider', 'Title', 'Review', 'Location', 'Created', 'Anonymous', 'Escalate', 'Rating', 'Name');

    $file = fopen('php://output', 'w');
    fputcsv($file, $columns);

    foreach($reviews as $review) {
        fputcsv($file, array($review->reviewID,$review->provider,$review->title,$review->review,$review->location,$review->review_created,$review->anon,$review->escalate,$review->rating,$review->name));
    }
    exit();
}

Есть ли что-то, что я делаю неправильно здесь, или у Laravel есть что-то, чтобы удовлетворить это?

Ответ 1

Попробуйте эту версию - это должно позволить вам получить хороший вывод с помощью Response::stream().

public function export()
{
    $headers = array(
        "Content-type" => "text/csv",
        "Content-Disposition" => "attachment; filename=file.csv",
        "Pragma" => "no-cache",
        "Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
        "Expires" => "0"
    );

    $reviews = Reviews::getReviewExport($this->hw->healthwatchID)->get();
    $columns = array('ReviewID', 'Provider', 'Title', 'Review', 'Location', 'Created', 'Anonymous', 'Escalate', 'Rating', 'Name');

    $callback = function() use ($reviews, $columns)
    {
        $file = fopen('php://output', 'w');
        fputcsv($file, $columns);

        foreach($reviews as $review) {
            fputcsv($file, array($review->reviewID, $review->provider, $review->title, $review->review, $review->location, $review->review_created, $review->anon, $review->escalate, $review->rating, $review->name));
        }
        fclose($file);
    };
    return Response::stream($callback, 200, $headers);
}

(Адаптировано из этого ответа SO: Использовать Laravel для загрузки таблицы как CSV)

Попробуйте использовать обычную ссылку с target="_blank" вместо использования JavaScript/AJAX. Поскольку это открытие загрузки файла на новой вкладке, пользовательский интерфейс не должен быть слишком неуклюжим.

Ответ 2

Это может не ответить на ваш вопрос напрямую, но я использую пакет, называемый thephpleague/csv 'для этой цели...

Чтобы использовать этот пакет:

  • композитор требует лиги /csv
  • Поместите следующие инструкции "use" в ваш контроллер:

    use Illuminate\Database\Eloquent\Collection;
    use League\Csv\Writer;
    use Schema;
    use SplTempFileObject;
    

    и любые классы моделей, которые вы планируете использовать.

  • Абстрактный CSV, создающий код для функции (в вашем контроллере), например:

    /**
     * A function to generate a CSV for a given model collection.
     *
     * @param Collection $modelCollection
     * @param $tableName
     */
    private function createCsv(Collection $modelCollection, $tableName){
    
        $csv = Writer::createFromFileObject(new SplTempFileObject());
    
        // This creates header columns in the CSV file - probably not needed in some cases.
        $csv->insertOne(Schema::getColumnListing($tableName));
    
        foreach ($modelCollection as $data){
            $csv->insertOne($data->toArray());
        }
    
        $csv->output($tableName . '.csv');
    
    }
    
  • В вашем контроллере создайте функцию get для извлечения/загрузки CSV (замените "MainMeta" на свой собственный класс модели):

    public function getMainMetaData(){
    
        $mainMeta = MainMeta::all();
    
        // Note: $mainMeta is a Collection object 
        //(returning a 'collection' of data from using 'all()' function), 
        //so can be passed in below.
        $this->createCsv($mainMeta, 'main_meta');
    }
    

    Когда вы создаете маршрут для вызова этой функции, он загружает CSV файл в вашем браузере, выбранной вами коллекции/данных Model.

  • Создайте маршрут в приложении \Http\routes.php следующим образом:

    Route::get(
        '/data/download/main_meta',
        [
            'as' => 'data/download/main_meta',
            'uses' => '[email protected]'
        ]
    );
    
  • (Необязательно) В файле вида лезвия (например, data.blade.php) укажите ссылку или кнопку, чтобы вы могли легко получить доступ к URL-адресу загрузки:

    <p><a href="{{ URL::route('data/download/main_meta') }}" class="btn btn-lg btn-primary pull-left">Download Main Meta Data</a></p>
    

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

Конечно, это будет отличаться в зависимости от вашего собственного приложения. С этим пакетом вы можете многое сделать (полная документация находится в http://csv.thephpleague.com/). Проект, в котором я использую это, находится в https://github.com/rattfieldnz/bitcoin-faucet-rotator - я только что начал кодировать его снова через несколько месяцев, поэтому все еще есть бит рефакторинга/тестирования/очистки до:).

Ответ 3

Попробуй это:

<?php

public function download()
{
    $headers = [
            'Cache-Control'       => 'must-revalidate, post-check=0, pre-check=0'
        ,   'Content-type'        => 'text/csv'
        ,   'Content-Disposition' => 'attachment; filename=galleries.csv'
        ,   'Expires'             => '0'
        ,   'Pragma'              => 'public'
    ];

    $list = User::all()->toArray();

    # add headers for each column in the CSV download
    array_unshift($list, array_keys($list[0]));

   $callback = function() use ($list) 
    {
        $FH = fopen('php://output', 'w');
        foreach ($list as $row) { 
            fputcsv($FH, $row);
        }
        fclose($FH);
    };

    return Response::stream($callback, 200, $headers); //use Illuminate\Support\Facades\Response;

}

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

Ответ 4

Мой подход в Laravel 5.7

/**
 * @param array $columnNames
 * @param array $rows
 * @param string $fileName
 * @return \Symfony\Component\HttpFoundation\StreamedResponse
 */
public static function getCsv($columnNames, $rows, $fileName = 'file.csv') {
    $headers = [
        "Content-type" => "text/csv",
        "Content-Disposition" => "attachment; filename=" . $fileName,
        "Pragma" => "no-cache",
        "Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
        "Expires" => "0"
    ];
    $callback = function() use ($columnNames, $rows ) {
        $file = fopen('php://output', 'w');
        fputcsv($file, $columnNames);
        foreach ($rows as $row) {
            fputcsv($file, $row);
        }
        fclose($file);
    };
    return response()->stream($callback, 200, $headers);
}

public function someOtherControllerFunction() {
    $rows = [['a','b','c'],[1,2,3]];//replace this with your own array of arrays
    $columnNames = ['blah', 'yada', 'hmm'];//replace this with your own array of string column headers        
    return self::getCsv($columnNames, $rows);
}

Ответ 5

Я сделал небольшой пакет LaravelCsvGenerator

и поместил его на упаковщика

Монтаж

$ composer require  eugene-melbourne/laravel-csv-generator

пример использования в вашем контроллере

class MyController extends Controller
{

    public function getCsv(): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $data    = [
                [1, 2.1],
                [3, "hi, there"],
            ];
        $headers = ['one', 'two'];
        $data = array_merge([$headers], $data);

        return (new \LaravelCsvGenerator\LaravelCsvGenerator())
                ->setData($data)
                ->renderStream();
    }

Пожалуйста, не стесняйтесь комментировать ваши идеи ниже этого ответа.