web-dev-qa-db-fra.com

Export CSV dans le contrôleur laravel 5

J'ai donc fait une petite requête ajax à mon reviewsController@export.

Maintenant, lorsque je console.log() les données dans ma méthode de réussite, la réponse ajax affiche les données correctes. Cependant, mon fichier CSV n'a pas été téléchargé. Donc, j'ai toutes les bonnes informations et j'ai essentiellement créé le CSV.

Je pense que cela a probablement à voir avec la définition des en-têtes peut-être?

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();
}

Y a-t-il quelque chose que je ne fais pas bien ici ou Laravel a-t-il quelque chose à offrir pour cela?

14
Matthew Smart

Essayez cette version - cela devrait vous permettre d’obtenir une sortie Nice en utilisant 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);
}

(Adapté de cette SO réponse: Utilisez Laravel pour télécharger le tableau au format CSV )

Essayez d'utiliser un lien régulier avec target="_blank" plutôt que d'utiliser JavaScript/AJAX. Comme il s’agit d’une ouverture de téléchargement de fichier dans un nouvel onglet, l’expérience utilisateur ne doit pas être trop lourde.

24
Nerdwood

Cela ne répond peut-être pas directement à votre question, mais j'utilise un package appelé ' thephpleague/csv ' à cette fin ...

Pour utiliser ce package:

  1. compositeur besoin de ligue/csv
  2. Mettez les instructions 'use' suivantes dans votre contrôleur:

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

    et toutes les classes de modèle que vous prévoyez d'utiliser.

  3. CSV abstrait créant du code pour une fonction (dans votre contrôleur), par exemple:

    /**
     * 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');
    
    }
    
  4. Dans votre contrôleur, créez une fonction get pour récupérer/télécharger un fichier CSV (remplacez 'MainMeta' par votre propre classe de modèle):

    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');
    }
    

    Lorsque vous créez une route pour appeler cette fonction, un fichier CSV, contenant les données de votre collection de modèles, est téléchargé dans votre navigateur.

  5. Créez un itinéraire dans App\Http\routes.php comme suit:

    Route::get(
        '/data/download/main_meta',
        [
            'as' => 'data/download/main_meta',
            'uses' => 'YourController@getMainMetaData'
        ]
    );
    
  6. (Facultatif) Dans un fichier de vue de lame (par exemple, data.blade.php), incluez un lien ou un bouton pour pouvoir accéder facilement à l'URL/route de téléchargement:

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

    Lorsque vous cliquez sur le lien, le fichier CSV sera téléchargé dans votre navigateur. Dans une application que j'ai codée, vous resterez sur la page sur laquelle vous cliquez sur ce lien.

Bien entendu, cela diffère en fonction de votre propre application. Vous pouvez faire beaucoup plus avec ce paquet (la documentation complète se trouve sur http://csv.thephpleague.com/ ). Le projet dans lequel j'utilise ceci se trouve à https://github.com/rattfieldnz/bitcoin-faucet-rotator - Je viens de recommencer à coder dessus après quelques mois, donc j'ai encore un peu de refactoring/tester/ranger à faire :).

2
Rob

Essaye ça:

<?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;

}
1
Luca C.

J'ai fait un petit paquet LaravelCsvGenerator

et l'a placé sur packagist

Installation

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

exemple d'utilisation dans votre contrôleur 

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();
    }

S'il vous plaît, n'hésitez pas à commenter vos idées ci-dessous cette réponse.

0
Yevgeniy Afanasyev