From eec5e01c9ebbca3db50793b0862a3cde7c2597b7 Mon Sep 17 00:00:00 2001 From: Lito Date: Fri, 29 Sep 2017 00:31:48 +0200 Subject: [PATCH] Added CSV Exporter --- src/Http/Controllers/ModelController.php | 82 ++++++++++++--- src/Http/routes.php | 16 +-- src/Services/Export/Csv.php | 121 +++++++++++++++++++++++ views/pages/index.blade.php | 19 +++- 4 files changed, 209 insertions(+), 29 deletions(-) create mode 100644 src/Services/Export/Csv.php diff --git a/src/Http/Controllers/ModelController.php b/src/Http/Controllers/ModelController.php index 7942260..dc0b1d2 100644 --- a/src/Http/Controllers/ModelController.php +++ b/src/Http/Controllers/ModelController.php @@ -3,13 +3,16 @@ namespace Anavel\Crud\Http\Controllers; use ANavallaSuiza\Laravel\Database\Contracts\Manager\ModelManager; +use ANavallaSuiza\Laravel\Database\Repository\Eloquent\Repository; use Anavel\Crud\Contracts\Abstractor\Model; use Anavel\Crud\Contracts\Abstractor\ModelFactory as ModelAbstractorFactory; use Anavel\Crud\Contracts\Controllers\CustomController; use Anavel\Crud\Contracts\Form\Generator as FormGenerator; use Anavel\Crud\Repository\Criteria\OrderByCriteria; use Anavel\Crud\Repository\Criteria\SearchCriteria; +use Anavel\Crud\Repository\Criteria\WithCriteria; use Anavel\Foundation\Http\Controllers\Controller; +use Anavel\Crud\Services\Export\Csv; use App; use Illuminate\Http\Request; @@ -28,7 +31,9 @@ public function __construct(ModelAbstractorFactory $modelFactory, ModelManager $ private function authorizeMethod(Model $modelAbstractor, $methodName) { - if (array_key_exists('authorize', $config = $modelAbstractor->getConfig()) && $config['authorize'] === true) { + $config = $modelAbstractor->getConfig(); + + if (array_key_exists('authorize', $config) && $config['authorize'] === true) { $this->authorize($methodName, $modelAbstractor->getInstance()); } } @@ -58,25 +63,22 @@ private function customController(Model $modelAbstractor) } /** - * Display a listing of the resource. + * Prepare contents to be used * * @param Request $request - * @param string $model + * @param Model $modelAbstractor + * @param Repository $repository * - * @return Response + * @return void */ - public function index(Request $request, $model) + public function getIndexRequirements(Request $request, Model $modelAbstractor, Repository $repository) { - $modelAbstractor = $this->modelFactory->getBySlug($model); - - $this->authorizeMethod($modelAbstractor, 'adminIndex'); - - if ($customController = $this->customController($modelAbstractor)) { - return $customController->index($request, $model); + foreach ($modelAbstractor->getListFields()['main'] as $field) { + if (strpos($field->getName(), '.')) { + $repository->pushCriteria(new WithCriteria(preg_replace('/\.[^\.]+$/', '', $field->getName()))); + } } - $repository = $this->modelManager->getRepository($modelAbstractor->getModel()); - if ($search = $request->input('search')) { $searchByColumns = []; @@ -91,6 +93,29 @@ public function index(Request $request, $model) $direction = $request->input('direction') ?: 'desc'; $repository->pushCriteria(new OrderByCriteria($sort, ($direction === 'desc') ? true : false)); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * @param string $model + * + * @return Response + */ + public function index(Request $request, $model) + { + $modelAbstractor = $this->modelFactory->getBySlug($model); + + $this->authorizeMethod($modelAbstractor, 'adminIndex'); + + if ($customController = $this->customController($modelAbstractor)) { + return $customController->index($request, $model); + } + + $repository = $this->modelManager->getRepository($modelAbstractor->getModel()); + + $this->getIndexRequirements($request, $modelAbstractor, $repository); return view('anavel-crud::pages.index', [ 'abstractor' => $modelAbstractor, @@ -98,6 +123,37 @@ public function index(Request $request, $model) ]); } + /** + * Display a listing of the resource. + * + * @param Request $request + * @param string $model + * + * @return Response + */ + public function exportCsv(Request $request, $model) + { + $modelAbstractor = $this->modelFactory->getBySlug($model); + + $this->authorizeMethod($modelAbstractor, 'exportCsv'); + + if ($customController = $this->customController($modelAbstractor)) { + return $customController->exportCsv($request, $model); + } + + $repository = $this->modelManager->getRepository($modelAbstractor->getModel()); + + $this->getIndexRequirements($request, $modelAbstractor, $repository); + + $csv = (string)(new Csv)->fromArray($repository->all()->toArray()); + + return response()->make($csv, 200, [ + 'Content-Encoding' => 'UTF-8', + 'Content-Type' => 'application/octet-stream', + 'Content-disposition' => 'attachment; filename='.$model.'.csv', + ]); + } + /** * Show the form for creating a new resource. * diff --git a/src/Http/routes.php b/src/Http/routes.php index ad796d9..6867653 100644 --- a/src/Http/routes.php +++ b/src/Http/routes.php @@ -17,6 +17,11 @@ function () { 'uses' => 'ModelController@index', ]); + Route::get('{model}/export/csv', [ + 'as' => 'anavel-crud.model.export.csv', + 'uses' => 'ModelController@exportCsv', + ]); + Route::get('{model}/create', [ 'as' => 'anavel-crud.model.create', 'uses' => 'ModelController@create', @@ -46,16 +51,5 @@ function () { 'as' => 'anavel-crud.model.destroy', 'uses' => 'ModelController@destroy', ]); - - // Batch actions - /*Route::put('{model}/batch', [ - 'as' => 'anavel-crud.model.batch-update', - 'uses' => 'ModelController@batchUpdate' - ]); - - Route::delete('{model}/batch', [ - 'as' => 'anavel-crud.model.batch-delete', - 'uses' => 'ModelController@batchDestroy' - ]);*/ } ); diff --git a/src/Services/Export/Csv.php b/src/Services/Export/Csv.php new file mode 100644 index 0000000..5de0695 --- /dev/null +++ b/src/Services/Export/Csv.php @@ -0,0 +1,121 @@ +open(); + + foreach ($data as $line) { + $this->headers($line); + } + + $this->write($this->headers); + + $fill = array_fill_keys($this->headers, ''); + + foreach ($data as $line) { + $this->write(array_merge($fill, $this->line($line))); + } + + $this->close(); + + return $this; + } + + public function __toString() + { + return $this->csv; + } + + private function open() + { + // Use memory as file + $this->fp = fopen('php://memory', 'w'); + + // Add BOM to fix UTF-8 in Excel + fwrite($this->fp, chr(0xEF).chr(0xBB).chr(0xBF)); + } + + private function write(array $data) + { + fputcsv($this->fp, $data); + } + + private function close() + { + rewind($this->fp); + + $this->csv = stream_get_contents($this->fp); + + fclose($this->fp); + } + + private function headers(array $data, $prefix = null) + { + $prefix = $prefix ? ($prefix.'.') : ''; + + foreach ($data as $key => $value) { + $key = $prefix.$key; + + if ($this->existsHeader($key)) { + continue; + } + + $isArray = is_array($value); + + if ($isArray && array_key_exists(0, $value)) { + continue; + } + + if ($isArray) { + $this->headers($value, $key); + } elseif ($this->isValidHeader($key)) { + $this->headers[] = $key; + } + } + } + + private function line(array $line, $prefix = null) + { + $data = []; + $prefix = $prefix ? ($prefix.'.') : ''; + + foreach ($line as $key => $value) { + $key = $prefix.$key; + $isArray = is_array($value); + + if ($isArray && array_key_exists(0, $value)) { + continue; + } + + if ($isArray) { + $data = array_merge($data, $this->line($value, $key)); + } elseif ($this->existsHeader($key)) { + $data[$key] = trim(str_replace(["\n", "\r"], '', $value)); + } + } + + return $data; + } + + private function isValidHeader($key) + { + return preg_match('/_id$/', $key) === 0; + } + + private function existsHeader($key) + { + return in_array($key, $this->headers, true); + } +} diff --git a/views/pages/index.blade.php b/views/pages/index.blade.php index 1e11c11..2d9445b 100644 --- a/views/pages/index.blade.php +++ b/views/pages/index.blade.php @@ -103,15 +103,24 @@ - @if ($items->total() > $items->perPage()) - @endif @stop -@section('footer-scripts') - @parent - +@section('footer-scripts') @parent + @stop