1

In my custom Joomla 5 component I've added PHPSpreadsheet via composer. In the Export View (Backend) i have a small adminform for setting up the export and a submit button that toggles the download by simply call the Method in the controller by task:

$route = Route::_('index.php?option=com_component&view=export');
...
<form action="<?php echo $route ?>" method="post" name="adminForm" id="adminForm">
...
<input type="hidden" name="task" value="export.export">

In my Export Model I call it like this:

public function export($settings):bool
    {
        // Required gets
        $this->countries = $this->getCountries();
        $this->app = Factory::getApplication();

        $fileName = 'export.xlsx';
        if(!empty($settings['filename'])){
            $fileName = $settings['filename'] . '.xlsx';
        }

        switch ($settings['export_type']) {
            case 'startlist':
            default:
                $arrayData = $this->exportStartList($settings);
                break;
        }

        if (empty($arrayData)) {
            $app = Factory::getApplication();
            $app->enqueueMessage(Text::_('COM_COMPONENT_EXPORT_NO_DATA'), 'warning');
            return false;
        }

        $spreadsheet = new Spreadsheet();
        $spreadsheet->getActiveSheet()->fromArray($arrayData);
        $writer = new Xlsx($spreadsheet);
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment; filename="' . urlencode($fileName) . '"');

        $writer->save('php://output');
        Factory::getApplication()->close();
        return true;
    }

My Problem is quite easy to explain:

  • To get the xlsx as download i have to call "exit" / "jexit" or "Factory::getApplication()->close();" > the download does work. But all enqueued messages got lost because of the changed header / the close itself (which makes sense for the CMS - i'ts closed so why rendering any messages now). The thing is: I've added some warnings if the dataset is not complete - it's not 100% necessary but now i'm at a point where i just WANT it to work :-).

Can anyone give me a hint on how to get both: initiate the download and render my enqueued messages on screen?

My Controller is minimalistic:

class ExportController extends FormController
{
    // Optional: Declare  view name
    protected $default_view = 'export';

    public function export()
    {
        error_log('Exporting in CONTROLLER');
        // Do some stuff here
        $data = $this->input->post->get('jform', [], 'array');
        $model = $this->getModel();

        if($model->export($data)) $this->setMessage(Text::_('COM_COMPONENT_EXPORT_SUCCESS'));

        $this->setRedirect('index.php?option=com_component&view=export');
    }

}

if the $arrayData is empty the message got displayed. I've tried it already with and without redirect in controller - no difference.

1 Answer 1

1

You could change the logic of your processing in the Model so that the spreadsheet is only presented if there is data retrieved to be displayed, in which case Closing the Application would make sense.

If messages are generated and stored in the Application then issuing a close() destroys the Application object and thus there is nothing left to display. The Application object has to remain available for you to receive the messages.

If you are trying to keep aligned with the MVC approach then I would create a View for Export(you appear to already have this) and move these lines from your Controller into the View to have your View get your data.

$model = $this->getModel();
if($model->export($data)) $this->setMessage(Text::_('COM_COMPONENT_EXPORT_SUCCESS'));

You will need to rethink how to get the Input from the previous page/form into the Model, maybe using the populateState() approach to store the $data with the Controller so that the values are available to the Model.

These lines in your Model are more relevant to the presentation of the Data once the data has been retrieved by the Model so to my mind they should be in the View class and or parts of in the View template.

$spreadsheet = new Spreadsheet();
$spreadsheet->getActiveSheet()->fromArray($arrayData);
$writer = new Xlsx($spreadsheet);
   header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
   header('Content-Disposition: attachment; filename="' . urlencode($fileName) . '"'); 
$writer->save('php://output');

So the processing would go more like the Controller gets the Input and redirects to the View, the View calls the Model to retrieve the data and then constructs the presentation of the spreadsheet if data is retrieved and if no data is returned then the export view will display the error/warning message.

Someone else may have a better way to handle the presentation of the Spreadsheet to suggest but moving the call to the Model from the Controller to the View and the presentation aspects from the Model to the View then you might find it easier to make adjustments and come up with alternative ways to provide the spreadsheet.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.