Oliver Maglana
Oliver Maglana

Reputation: 25

How to export file directly on OctoberCms

I have create a custom page that looks like this so all function are custom. I wanted to know how to directly export the results that are shown on my table using my export button without having to redirect on the export page because the default function redirects you to the certain page and that's what I am avoiding in order to retrieve also the filters that and apply those on the export function.

I have created a call exportOrders it has no function on the Controller yet because I have no idea what to do with it. enter image description here

Controller .htm

<?= Form::open(['class' => 'layout']) ?>
<div class="filter-container">
    <div class="row">
        <div class="status-container column">
            <label for="status">Status:</label>
            <select id="statusFilter" class="form-control custom-select" name="status">
                <option value="none" selected>None</option>
                <option value="processing">Processing</option>
            </select>
        </div>
        (...)
    </div>
    <button>
    (...)
    </button>
    <button
        id="exportOrders"
        type="submit"
        data-request="onExportOrders"
        data-hotkey="ctrl+s, cmd+s"
        data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
        class="btn btn-secondary">
        Export
    </button>
    
</div>

<div class="orders-table">
    <table class="table d-flex justify-content-center">
        <thead>
            <th>Order ID</th>
            <th>Product Name</th>
            <th>Status</th>
            <th>Payment Method</th>
            <th>SKU</th>
            <th>Total</th>
        </thead>
        <tbody>
            <?php foreach($orders as $data) {
                # declare variables here
                $id = $data['id'];
                $status = $data['status'];
                $payment_gateway = $data['payment_gateway'];
                $total = $data['total'];
                $sku = $data['sku'];
                $product_name = $data['product_name'];

                echo("<tr>
                        <td><a target='' href='/admin/xtendly/ecommerce/orders/update/$data[id]'>$id</a></td>
                        <td>$product_name</td>
                        <td>$status</td>
                        <td>$payment_gateway</td>
                        <td>$total</td>
                        <td>$sku</td>
                    </tr>");
            } ?>
        </tbody>
    </table>
</div>
<?= Form::close() ?>

This is my Controller for handling the results

public $implement = [        
        'Backend\Behaviors\ListController',        
        'Backend\Behaviors\FormController',        
        'Backend\Behaviors\ReorderController'    
    ];
    
    public $listConfig = 'config_list.yaml';
    public $formConfig = 'config_form.yaml';
    public $reorderConfig = 'config_reorder.yaml';

    public function __construct()
    {
        parent::__construct();
        BackendMenu::setContext('X.Ecommerce', 'main-menu-item2');
    }

    public function partialRenderOrders()
    {
        $status = post('status');
        $payment = post('payment');
        
        $data = $this->getOrders($status, $payment);
        
        $params = [
            'orders' => $data,
        ];

        return $this->makePartial('orders', $params);
    }

    public function getOrders($status_ = null) {
        $query = Order::query();

        if($status_ !== "none" || $status_ == null) {
            $result = $query->where('status','LIKE','%'.$status_.'%')->get();
        } else {
            $result = $query->get();
        }
        
        $collection = collect($result->toArray());
        $data = $collection->map(function ($item) {
            if(is_array($item)) {
                foreach($item as $key => $value) {
                    
                    //Order Details
                    if($key == 'order_details') {
                        $order_details = json_decode($value);
                        $sku_ = ""; $product_name = "";
                        $last = count($order_details) - 1;
                        for($i = 0; $i < count($order_details); $i++) {
                            if($last > $i) {
                                //sku
                                $sku_.= isset($order_details[$i]->sku) && $order_details[$i]->sku !== "" ? "".strval($order_details[$i]->sku).", " : '';
                                //product name
                                $product_name = isset($order_details[$i]->title) && $order_details[$i]->title !== "" ? "".strval($order_details[$i]->title).", " : '';
                            } else {
                                //sku
                                $sku_.= isset($order_details[$i]->sku) && $order_details[$i]->sku !== "" ? "".strval($order_details[$i]->sku)."" : '';
                                //product name
                                $product_name.= isset($order_details[$i]->title) && $order_details[$i]->title !== "" ? "".strval($order_details[$i]->title)."" : '';
                            }
                        }
                        $item['sku'] = $sku_;
                        $item['product_name'] = $product_name;
                    }
                }
            }
            return $item;
        });
        return $data;
    }

It would be a big help if you could provide me a good code since I'm quite new with OctoberCMS.

Upvotes: 1

Views: 578

Answers (1)

Hardik Satasiya
Hardik Satasiya

Reputation: 9715

The best solution would be to just add your own controller action to export data.

Main Ref: https://tutorialmeta.com/october-cms/october-cms-export-csv

In toolbar add a new button which will point to your controller's action

<button
    onclick="{
        const status = 'active';// use selector to get value $('#statusFilter').val();
        window.location = `<?= Backend::url('hardik/sotest/items/export') ?>/${status}`;
    }"
    class="btn btn-primary oc-icon-sign-out">
    Export
</button>

Note: Please replace author name/plugin name and controller name as per your need in the URL. If you do not need to pass any filter values you can directly use a href link.

Controller side.

<?php
//..some code

use League\Csv\Writer as CsvWriter;
use October\Rain\Parse\League\EscapeFormula as CsvEscapeFormula;

class Items extends Controller
{
    //.. some code
    
    // CUSTOM CSV EXPORT logic
    // only write $status param if you intend to pass it for filter
    public function export($status) 
    {
        // dd($status); -> O/P => active
        // $status will have value which was passed while calling URL as param
        // now use this to filter your data
        // you can add as many params you need
    
        // file name when you download CSV
        $fileName = 'export.csv';

        // prepare CSV writer
        $csv = CsvWriter::createFromFileObject(new \SplTempFileObject);
        $csv->setOutputBOM(CsvWriter::BOM_UTF8);
        $formatter = new CsvEscapeFormula();

        // add headers as per your need
        $csv->insertOne(['Name', 'Flower']);

        // add records
        // here I added some demo data you need to fetch from DB or something
        // probabally use $results = $this->getOrders($status); here
        $results = [
          [
            'name' => 'First Name',
            'flower' => 'Rose'
          ],
          [
            'name' => 'Second Name',
            'flower' => 'Lavender'
          ],
        ];

        // loop through your records and add to csv rows
        foreach ($results as $result) {
            // formate data and escape quotes etc
            $data = $formatter($result);

            // add single record to CSV
            $csv->insertOne([
              $data['name'], // this will be name
              $data['flower'] // this will be flower
            ]);
        }

        // Save for download
        $csvName = uniqid('hs');
        $csvPath = temp_path().'/'.$csvName;
        $output = $csv->__toString();
        \File::put($csvPath, $output);

        // download it
        return \Response::download($csvPath, $fileName)->deleteFileAfterSend(true);
    }
}

Note: In this example I added demo data for the generic purpose you can replace it and use your own data source and loop through them and add them to the CSV file.

Now when you click on the Export button it will let you download the export.csv file. and its content will be as below.

enter image description here

if any doubt please comment.

Upvotes: 1

Related Questions