How do I download a generated csv in magento 2.4.5 - csv

My files so far are:
system.xml
<field id="generate_csv" translate="label" type="button" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Generate Categories CSV</label>
<frontend_model>Vendor\Module\Block\Adminhtml\System\Config\Button</frontend_model>
</field>
button.php
<?php
namespace [Vendor]\[Module]\Block\Adminhtml\System\Config;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Button extends Field
{
protected $_template = 'Vendor_Module::system/config/button.phtml';
public function __construct(
Context $context,
array $data = []
){
parent::__construct($context, $data);
}
public function render(AbstractElement $element)
{
$element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
return parent::render($element);
}
protected function _getElementHtml(AbstractElement $element)
{
return $this->_toHtml();
}
public function getAjaxUrl()
{
return $this->getUrl('[router]/Export/CategoryExport');
}
public function getButtonHtml()
{
$button = $this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Button'
)->setData(
[
'id' => 'generate_csv',
'label' => __('Generate CSV')
]
);
return $button->toHtml();
}
}
button.phtml
<script>
require([
'jquery',
'prototype'
], function ($) {
$('#generate_csv').click(function () {
var params = {};
new Ajax.Request('<?php echo $block->getAjaxUrl() ?>', {
parameters: params,
loaderArea: false,
asynchronous: true,
onCreate: function () {
$('#custom_button_response_message').text('');
},
onSuccess: function (transport) {
var resultText = '';
if (transport.status > 200) {
resultText = transport.statusText;
} else {
var response = JSON.parse(transport.responseText);
resultText = response.message
}
$('#generate_csv_response_message').text(resultText);
}
});
});
});
</script>
<?php echo $block->getButtonHtml(); ?>
<p>
<span id="generate_csv_response_message"></span>
</p>
controller
<?php
namespace [Vendor]\[Module]\Controller\Adminhtml\Export;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\Json;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
use Magento\Catalog\Model\CategoryFactory;
class CategoryExport extends Action
{
protected $resultJsonFactory;
/**
* #param Context $context
* #param JsonFactory $resultJsonFactory
*/
public function __construct(
Context $context,
JsonFactory $resultJsonFactory,
Filesystem $filesystem,
DirectoryList $directoryList,
CollectionFactory $categoryCollectionFactory,
CategoryFactory $categoryFactory
){
$this->resultJsonFactory = $resultJsonFactory;
$this->filesystem = $filesystem;
$this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
$this->categoryCollectionFactory = $categoryCollectionFactory;
$this->categoryFactory = $categoryFactory;
parent::__construct($context);
}
/**
* #return Json
*/
public function execute()
{
$message = 'Success';
$data[] = [
'id' => __('Entity ID'),
'name' => __('Name'),
'level' => __('Level')
];
$categoryCollection = $this->categoryCollectionFactory->create();
$categoryCollection->addAttributeToSelect('*');
$filepath = 'export/customerlist.csv';
$this->directory->create('export');
$stream = $this->directory->openFile($filepath, 'w+');
$stream->lock();
$header = ['Id', 'Name', 'Level'];
$stream->writeCsv($header);
//$categoryArray = array();
foreach ($categoryCollection as $category) {
$data = [];
$data[] = $category->getId();
$data[] = $category->getName();
$data[] = $category->getLevel();
$stream->writeCsv($data);
}
/** #var Json $result */
$result = $this->resultJsonFactory->create();
return $result->setData(['message' => $message]);
}
}
This creates a csv file in var/export directory but I cannot download it
Can anyone help explain how to modify the code so that it will automatically create and down the csv.
I have tried following various examples on the web but none of them have worked.
Please help me out

Related

Download CSV in Mezzio Framework (Zend/Laminas)

In Mezzion Framework I have the next Handler:
<?php
namespace Bgc\Handler;
use App\Service\GenerateReportToCSV;
use Bgc\Queue\BGCQueueManager;
use Laminas\Diactoros\Response\TextResponse;
use League\Csv\Writer;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class DownloadBgcReportHandler implements RequestHandlerInterface
{
protected $bgcQManager;
protected $reportToCSV;
public function __construct(BGCQueueManager $bgcQManager, $reportToCSV)
{
$this->bgcQManager = $bgcQManager;
$this->reportToCSV = $reportToCSV;
}
public function handle(ServerRequestInterface $request): TextResponse
{
$queryParams = $request->getQueryParams();
$params = [];
if (isset($queryParams['startDate'])) {
$starDate = new \DateTime($queryParams['startDate']);
$params['startDate'] = $starDate->modify('midnight');
}
if (isset($queryParams['startDate'])) {
$endDate = new \DateTime($queryParams['endDate']);
$params['endDate'] = $endDate->modify('tomorrow');
}
$itemsBGC = $this->bgcQManager->getDataToDownload($params);
$time = time();
$fileName = "bgc-report-$time.csv";
$csv = Writer::createFromFileObject(new \SplFileObject());
$csv->insertOne($this->reportToCSV->getHeadingsBGC());
foreach ($itemsBGC as $item) {
$csv->insertOne($item);
}
return new TextResponse($csv->getContent(), 200, [
'Content-Type' => 'text/csv',
'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' => "attachment; filename='$fileName'"
]);
}
}
I have the below error:
Whoops\Exception\ErrorException: Declaration of Bgc\Handler\DownloadBgcReportHandler::handle(Psr\Http\Message\ServerRequestInterface $request): Laminas\Diactoros\Response\TextResponse must be compatible with Psr\Http\Server\RequestHandlerInterface::handle(Psr\Http\Message\ServerRequestInterface $request): Psr\Http\Message\ResponseInterface in file /home/peter/proyectos/revelations-thena-api/src/Bgc/src/Handler/DownloadBgcReportHandler.php on line 20
I don't know, to create a downloable file. The hadbler works fine with Json. I tried to change from ResponseInterface to TextResponse.
How can I download file CSV?
Thank you
The error you received is telling you that your method signature is not compliant to interface's method signature.
RequestHandlerInterface:
interface RequestHandlerInterface
{
public function handle(ServerRequestInterface $request): ResponseInterface;
}
As you see, the signature states that an object of type ResponseInterface is returned.
You modified the signature:
class DownloadBgcReportHandler implements RequestHandlerInterface
{
public function handle(ServerRequestInterface $request): TextResponse;
}
The signature must be the same, but then you can return the TextResponse without problem (since it extends Laminas\Diactoros\Response, which implements Psr\Http\Message\ResponseInterface)
Just change that and it will works :)
You have modified you handle method, so right now you aren't fulfilling the requirements of the RequestHandlerInterface
Replace the return value for the handler with ResponseInterface enforced in the interface: RequestHandlerInterface
so i think you are best helped with:
<?php
namespace Bgc\Handler;
use App\Service\GenerateReportToCSV;
use Bgc\Queue\BGCQueueManager;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\Stream;
use League\Csv\Writer;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class DownloadBgcReportHandler implements RequestHandlerInterface
{
protected $bgcQManager;
protected $reportToCSV;
public function __construct(BGCQueueManager $bgcQManager, $reportToCSV)
{
$this->bgcQManager = $bgcQManager;
$this->reportToCSV = $reportToCSV;
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$queryParams = $request->getQueryParams();
$params = [];
if (isset($queryParams['startDate'])) {
$starDate = new \DateTime($queryParams['startDate']);
$params['startDate'] = $starDate->modify('midnight');
}
if (isset($queryParams['startDate'])) {
$endDate = new \DateTime($queryParams['endDate']);
$params['endDate'] = $endDate->modify('tomorrow');
}
$itemsBGC = $this->bgcQManager->getDataToDownload($params);
$time = time();
$fileName = "bgc-report-$time.csv";
// $csv = Writer::createFromFileObject(new \SplFileObject());
// $csv->insertOne($this->reportToCSV->getHeadingsBGC());
$csv = Writer::createFromString($this->reportToCSV->getHeadingsBGC());
foreach ($itemsBGC as $item) {
$csv->insertOne($item);
}
$body = new Stream($csv->getContent());
return new Response($body, 200, [
'Cache-Control' => 'must-revalidate',
'Content-Disposition' => 'attachment; filename=' . $fileName,
'Content-Length' => strval($body->getSize()),
'Content-Type' => 'text/csv',
'Content-Transfer-Encoding' => 'binary',
'Expires' => '0',
'Pragma' => 'public',
]);
}
}
PS: i have commented the 2 lines in which an empty new \SplFileObject() was used, because the required param $filename was empty (and i did not want to make a decision there) and added a line with Writer::createFromString().

How can i insert data into two MySql tables on a single click?

How can i insert data into two MySql tables on a single click ?
model code
public function insertCSV($data)
{
$this->db->insert('question_tb', $data);
$this->db->insert('option_tb', $data);
return TRUE;
}
controller code
public function import(){
if (isset($_POST["import"])) {
$this->load->model('Welcome_model', 'welcome');
$exam_id = $this->welcome->get_max_exam_id();
$filename = $_FILES["file"]["tmp_name"];
if ($_FILES["file"]["size"] > 0) {
$file = fopen($filename, "r");
while (($importdata = fgetcsv($file, 10000, ",")) !== false) {
$data = [
'question' => $importdata[0],
'answer' => $importdata[1],
'exam_id' => $exam_id,
];
while (($importdata = fgetcsv($file, 10000, ",")) !== false) {
$data = [
'option1' => $importdata[2],
'option2' => $importdata[3],
'option3' => $importdata[4],
'exam_id' => $exam_id,
];
$insert = $this->welcome->insertCSV($data);
}
//fclose($file);
fclose($file);
redirect('upload/index');
}
}
}
}
There are two options
Create two functions in the model and call those function in the controller.
Model:
function a($data1){
$this->db->insert('table1', $data1);
}
function b($data2){
$this->db->insert('table2', $data2);
}
Controller :
function insert(){
$this->model_name->a($data1);
$this->model_name->b($data2);
}
Create a function in the model and pass two array data
Model:
function a($data1, $data2){
$this->db->insert('table1', $data1);
$this->db->insert('table2', $data2);
}
Controller:
function insert(){
$this->model_name->a($data1, $data2);
}

Import Excel in Yii2

I have yii2 application using advanced template and database mySql, i already make function for import an excel file to one of the table,
I made the function in a controller named student that contains CRUD of students data.this is my code
public function actionImportExcel()
{
$inputFile = 'uploads/siswa_file.xlsx';
try{
$inputFileType = \PHPExcel_IOFactory::identify($inputFile);
$objReader = \PHPExcel_IOFactory::createReader($inputFileType);
$objPHPExcel = $objReader->load($inputFile);
} catch (Exception $e) {
die('Error');
}
$sheet = $objPHPExcel->getSheet(0);
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
for($row=1; $row <= $highestRow; $row++)
{
$rowData = $sheet->rangeToArray('A'.$row.':'.$highestColumn.$row,NULL,TRUE,FALSE);
if($row==1)
{
continue;
}
$siswa = new Siswa();
$siswa->nis = $rowData[0][0];
$siswa->nama_siswa = $rowData[0][1];
$siswa->jenis_kelamin = $rowData[0][2];
$siswa->ttl = $rowData[0][3];
$siswa->alamat = $rowData[0][4];
$siswa->telp = $rowData[0][5];
$siswa->agama = $rowData[0][6];
$siswa->nama_ortu = $rowData[0][7];
$siswa->telp_ortu = $rowData[0][8];
$siswa->pekerjaan_ortu = $rowData[0][9];
$siswa->tahun_masuk = $rowData[0][10];
$siswa->kelas = $rowData[0][11];
$siswa->save();
print_r($siswa->getErrors());
}
die('okay');
}
but i don't know how to make a button in a view to make this function work. i mean i want to make a button that when the user click the button and browse their excel file they can import that file and the data inside the excel can import to database
First you should upload the file
and then processing with your function
there are several parts of code you must produce ..
eg a view for the user to upload the file
View: #app/views/site/upload.php
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
<?= $form->errorSummary($model); ?>
<?= $form->field($model, 'imageFile')->fileInput() ?>
<button>Submit</button>
<?php ActiveForm::end() ?>
Controller: #app/controllers/SiteController.php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\UploadForm;
use yii\web\UploadedFile;
class SiteController extends Controller
{
public function actionUpload()
{
$model = new UploadForm();
if (Yii::$app->request->isPost) {
$model->imageFile = UploadedFile::getInstance($model, 'imageFile');
if ($model->upload()) {
// file is uploaded successfully
return;
}
}
return $this->render('upload', ['model' => $model]);
}
}
Model: #app/models/UploadForm.php
namespace app\models;
use yii\base\Model;
use yii\web\UploadedFile;
class UploadForm extends Model
{
/**
* #var UploadedFile
*/
public $imageFile;
public function rules()
{
return [
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
];
}
public function upload()
{
if ($this->validate()) {
$this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);
return true;
} else {
return false;
}
}
}
the code is from this doc

Laravel error:: Illegal string offset 'category_name'

I'm trying to create an admin side form. Here i'm trying to select data from the database. Also i want to display it. But for some reason it's not working. Here's my controller code,
public function save()
{
if (Input::has('save'))
{
$rules = array('category_name' => 'required|min:1|max:50', 'parent_category' => 'required');
$input = Input::all();
$messages = array('category_name.required' =>'Please enter the category name.', 'category_name.min' => 'Category name must be more than 4 characters', 'category_name.max' =>'Category name must not be more than 15 characters!!!', 'parent_category.required' => 'Please Select Parent Category.',);
$validator = Validator::make($input, $rules, $messages);
if ($validator->fails())
{
return Redirect::to('admin/category/add')->withErrors($validator)->withInput($input);
}
else
{
$insert_db = CategoryModel::insert($input);
$selected_category = CategoryModel::select($input['category_name']);
}
}
}
and my CategoryModel.php is following.
public static function insert($values)
{
$insert = DB::table('add_category')->insert(array('category_name'=>$values['category_name'], 'parent_category'=>$values['parent_category']));
if(isset($insert))
{
echo 'inserted successfully';
}
else
{
echo 'Failed';
}
}
public static function select($values)
{
$insert = DB::table('add_category')->where('category_name' . '=' . $values['category_name']);
}

How to access configs from autoloaded config files in a layout / view script in Zend Framework 2?

I would like / have to manage some settings in ZF1 style and provide the view with the infomation about the current environment.
/config/application.config.php
return array(
...
'module_listener_options' => array(
...
'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php')
)
);
/config/autoload/env.local.php
return array(
// allowed values: development, staging, live
'environment' => 'development'
);
In a common view script I can do it over the controller, since the controllers have access to the Service Manager and so to all configs I need:
class MyController extends AbstractActionController {
public function myAction() {
return new ViewModel(array(
'currentEnvironment' => $this->getServiceLocator()->get('Config')['environment'],
));
}
}
Is it also possible to get the configs in a common view directly?
How can I access the configs in a layout view script (/module/Application/view/layout/layout.phtml)?
(My implementation/interpretation of) Crisp's suggestion:
Config view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class Config extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
public function __invoke() {
$config = $this->serviceManager->getServiceLocator()->get('Config');
return $config;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'config' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\Config($serviceManager);
return $helper;
},
)
);
}
Layout view script
// do whatever you want with $this->config()['environment'], e.g.
<?php
if ($this->config()['environment'] == 'live') {
echo $this->partial('partials/partial-foo.phtml');;
}
?>
My implementation/interpretation of Sam's solution hint for the concret goal (to permit displaying the web analytics JS within the dev/staging environment):
DisplayAnalytics view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class DisplayAnalytics extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
public function __invoke() {
if ($this->serviceManager->getServiceLocator()->get('Config')['environment'] == 'development') {
$return = $this->view->render('partials/partial-bar.phtml');
}
return $return;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'displayAnalytics' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\DisplayAnalytics($serviceManager);
return $helper;
}
)
);
}
Layout view script
<?php echo $this->displayAnalytics(); ?>
So this solution becomes more flexible:
/config/application.config.php
return array(
...
'module_listener_options' => array(
...
'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php')
)
);
/config/autoload/whatever.local.php and /config/autoload/whatever.global.php
return array(
// allowed values: development, staging, live
'environment' => 'development'
);
ContentForEnvironment view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class ContentForEnvironment extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
/**
* Returns rendered partial $partial,
* IF the current environment IS IN $whiteList AND NOT IN $blackList,
* ELSE NULL.
* Usage examples:
* Partial for every environment (equivalent to echo $this->view->render($partial)):
* echo $this->contentForEnvironment('path/to/partial.phtml');
* Partial for 'live' environment only:
* echo $this->contentForEnvironment('path/to/partial.phtml', ['live']);
* Partial for every environment except 'development':
* echo $this->contentForEnvironment('path/to/partial.phtml', [], ['development', 'staging']);
* #param string $partial
* #param array $whiteList
* #param array $blackList optional
* #return string rendered partial $partial or NULL.
*/
public function __invoke($partial, array $whiteList, array $blackList = []) {
$currentEnvironment = $this->serviceManager->getServiceLocator()->get('Config')['environment'];
$content = null;
if (!empty($whiteList)) {
$content = in_array($currentEnvironment, $whiteList) && !in_array($currentEnvironment, $blackList)
? $this->view->render($partial)
: null
;
} else {
$content = !in_array($currentEnvironment, $blackList)
? $this->view->render($partial)
: null
;
}
return $content;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'contentForEnvironment' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\ContentForEnvironment($serviceManager);
return $helper;
}
)
);
}
Layout view script
<?php echo $this->contentForEnvironment('path/to/partial.phtml', [], ['development', 'staging']); ?>