Export Data Option for Custom Modules
Any idea where to start to allow for a data export option button for custom simple modules? Not sure if you have something developed already for this or not. I suppose it would be as simple as creating a custom button that would run a SQL query, but a short walkthrough would help me wrap my brain around how to do that.
Thanks!
Erik
Comments
One example I can give you that I've done is from a button I added to the dashboard. This button posted to a controller. In the controller I had (excuse the straight copy paste..)
public function export_dwd() { if (empty($_POST)) show_404(); $this->load->dbutil(); $query = $this->db ->select('e.title, e.name, e.email, e.phone_number, e.organisation_name, e.key_contact_person, e.provider_website, e.provider_email, e.provider_phone_number, e.provider_postal_address, e.description, e.duration, e.entrance_requirements, e.disabled, e.estimated_cost, e.published, e.date_added, e.date_published, e.state, t.name as type, GROUP_CONCAT(DISTINCT l.name) AS locations, GROUP_CONCAT(DISTINCT k.name) AS keywords, GROUP_CONCAT(DISTINCT f.name) AS formats', false) ->from('dwd_entries e') ->join('dwd_types t', 't.id = e.type_id') ->join('dwd_entries_to_locations etl', 'etl.entry_id = e.id', 'left') ->join('dwd_locations l', 'l.id = etl.location_id', 'left') ->join('dwd_entries_to_keywords etk', 'etk.entry_id = e.id', 'left') ->join('dwd_keywords k', 'k.id = etk.keyword_id', 'left') ->join('dwd_entries_to_formats etf', 'etf.entry_id = e.id', 'left') ->join('dwd_formats f', 'f.id = etf.format_id', 'left') ->group_by('e.id') ->get(); $this->output = $this->dbutil->csv_from_result($query, ','); $this->_download(); } private function _download() { header("Cache-Control: private"); header("Content-type: text/csv"); header("Content-Disposition: attachment; filename=\"".uniqid().'.csv'."\""); header("Content-type: application/x-msdownload"); echo $this->output; exit(); }
http://codeigniter.com/user_guide/database/utilities.html
You would need to create a controller to handle the backing up. The backup module's controller may be a good place to look for reference because it does something similar (but backs up the entire database).
After setting up the controller, you could add an "others" value to your modules "item_actions" parameter. The pages module does this for the "Upload" button that appears. You can see this in the fuel/modules/fuel/config/fuel_modules.php file.
'item_actions' => array('save', 'view', 'publish', 'delete', 'duplicate', 'create', 'others' => array('my_module/backup' => 'Backup')),
The last issue remaining has to do with how this function is accessed via a custom button I have created using the "list_actions" option in the MY_fuel_modules.php module declaration statement. Here is the config for the helmets module:
$config['modules']['helmets'] = array( 'module_name' => 'Helmets', 'module_uri' => 'helmets', 'model_name' => 'helmets_model', 'model_location' => 'store', 'permission' => 'helmets', 'instructions' => lang('module_instructions_default', 'helmets'), 'archivable' => FALSE, 'nav_selected' => 'helmets', 'hidden' => TRUE, 'table_actions' => array('edit', 'view'), 'item_actions' => array('others' => array('/store/helmets/export_non_shipped_orders' => 'Export'), 'save', 'view', ), 'list_actions' => array('/store/helmets/export_non_shipped_orders' => 'Export Orders To Ship') );
BTW, it took me a bit of trial and error to fully understand how to utilize the list_actions option, since the User Guide doesn't give any detail about how to use it.
Anyway, my last hangup issue here is to get the /store/helmets/export_non_shipped_orders function to execute properly from the generated button. The resulting html formats the hyperlink the button points to "/fuel/store/helmets/export_non_shipped_orders", which doesn't work. The URL that does work is /store/helmets/export_non_shipped_orders".
The code for my helmets controller stored at /modules/store/controllers/helmets.php is here:
<?php if(!defined('BASEPATH')) exit('No direct script access allowed'); require_once(FUEL_PATH.'/libraries/Fuel_base_controller.php'); class Helmets extends Fuel_base_controller { function __construct() { parent::__construct(); } /** * Export Non-Shipped Orders * Exports all helmet orders that are yet to be shipped. * * @return void * @author erikharper */ function export_non_shipped_orders() { // Load CI Database Utilities Class $this->load->dbutil(); // Write the Query $this->db->where('shipped', 'no'); $query = $this->db->get('helmets'); // Return Query Results $csv = $this->dbutil->csv_from_result($query); // Load Download Helper $this->load->helper('download'); // Force the Download of the File force_download('helmets.csv', $csv); } function index() { } }
Notice that it inherits from Fuel_base_controller. This function works when accessing it via /store/helmets/export_non_shipped_orders but NOT from /fuel/store/helmets/export_non_shipped_orders.
How can I set up my controller properly so that the "/fuel/store/helmets/export_non_shipped_orders" URL actually works instead of the other one?
Thanks
Erik
Basically, I just want to know how to create a custom controller in an advanced module (a module with its own folder in the modules folder) that can be accessed via a "/fuel" URL.
You'd need to create an advanced module named store with a controller of helmets::export_non_shipped_orders().
The controller would look something like:
require_once(FUEL_PATH.'/libraries/Fuel_base_controller.php'); class Helmets extends Fuel_base_controller { function __construct() { parent::__construct(); } public function export_non_shipped_orders() { # Do work here } }
That's how my example above of export_dwd() works.
You'd still want to stick a route in {module_name}_routes, something like:
$route[FUEL_ROUTE.'{module_name}/(.*)'] = {module_name}_FOLDER.'/helmets/$1';
Is that more helpful?
I can give you an example of a full controller for an advanced module that doesn't use FUEL's default module.php controller but it's quite long. It overwrites the create, edit and form methods. Let me know if that would be helpful.
$route[FUEL_ROUTE.'tools/search'] = 'search/search_module'; $route[FUEL_ROUTE.'tools/search/reindex'] = 'search/search_module/reindex';
If you have an advanced module that has several controllers that need to be created for the backend, like the blog, then you can setup routes in your fuel/modules/{module}/config/{module}_routes.php file. Below is what is used for the blog:
<?php $blog_controllers = array('posts', 'comments', 'categories', 'links', 'users'); foreach($blog_controllers as $c) { $route[FUEL_ROUTE.'blog/'.$c] = FUEL_FOLDER.'/module'; $route[FUEL_ROUTE.'blog/'.$c.'/(.*)'] = FUEL_FOLDER.'/module/$1'; } $route[FUEL_ROUTE.'blog/settings'] = BLOG_FOLDER.'/settings';
Also, make sure you add your module name (folder name) to the "modules_allowed" fuel config parameter in fuel/application/config/MY_fuel.php
$route[FUEL_ROUTE . '/store/helmets/(.*)'] = '/' . STORE_FOLDER . '/helmets/$1';
I still go back to wondering how I can create a custom button on my Fuel module list_items screen that simply loads this custom function without having to do any custom routing? Why can't fuel figure out where this function resides even if it has a /fuel in the URL?
Also, lance's other idea regarding changing line #61 in FUEL's module_list layout sounds appealing, but I don't want to hack the core code to make this happen. A little guidance in this regard is much appreciated.
There should be a way to create a button on a custom module's screen in the header that can point anywhere, that's really what I need.
Erik
Does it work when you remove the leading slash? And the STORE_FOLDER constant is set to store? (sorry to ask, you never know..)
$route[FUEL_ROUTE.'/store/helmets/(.*)'] = STORE_FOLDER.'/helmets/$1';
My question to the Fuel guys is this: How do I make a custom Fuel button work? Said another way, where do I put my code to make a custom button "do" something?
I just did a quick test in my advanced module (members) and it worked fine for me. My steps:
1) members_fuel_modules.php:
$config['modules']['members_members'] = array( 'module_name' => lang('members_mod_members'), 'module_uri' => 'members/members', 'model_name' => 'members_model', 'model_location' => MEMBERS_FOLDER, 'default_col' => 'email', 'display_field' => 'email', 'table_headers' => array( 'id', 'email', 'date_added', 'active' ), 'nav_selected' => 'members/members', 'permission' => 'members/members', 'js_controller' => 'UserController', 'configuration' => array('members' => 'members'), 'list_actions' => array(MEMBERS_FOLDER.'/helmets/export_non_shipped_orders' => 'Export Orders To Ship') );
2) Created a route for it in members_routes:
$route[FUEL_ROUTE.'members/helmets/(.*)'] = MEMBERS_FOLDER.'/helmets/$1';
3) Created the following controller:
<?php class Helmets extends CI_controller { function __construct() { parent::__construct(); } public function export_non_shipped_orders() { echo 'boom'; } }
I click the button above the data table and I get taken to http://localhost/test/fuel/members/helmets/export_non_shipped_orders.
I also see 'boom' printed to the screen.
As an aside, awesome ability to add those custom buttons! Didn't know I could do that up there..
$config['modules']['helmets'] = array( ....... 'list_actions' => array('store/helmets/export_non_shipped_orders' => 'Export Orders To Ship') );
Thanks for working through this with me. Learning Fuel is an on-going process, especially this module development stuff.
My next task is figuring how how I can customize the routes of my Store sub-modules in Fuel. I can't seem to duplicate how the blog does it with all of the sub-modules. Here is my current code and setup:
Module setup:
Store (parent module, does not map to its own database table. it is a container for the other two submodules)
- gift_certificates (child module, maps to the gift_certificates table)
- helmets (child module, maps to the helmets table)
Currently these routes work in Fuel:
/fuel/gift_certificates
/fuel/helmets
I would like to get these URLs to work:
/fuel/store/gift_certificates
/fuel/store/helmets
Here is my current store_routes.php file:
<?php $controllers = array('gift_certificates', 'helmets'); foreach($controllers as $c) { $route[FUEL_ROUTE . 'store/' . $c] = FUEL_FOLDER . '/' . $c; $route[FUEL_ROUTE . 'store/' . $c . '/(.*)'] = FUEL_FOLDER . '/' . $c . '/$1'; } $route[FUEL_ROUTE . 'store/helmets/(.*)'] = STORE_FOLDER . '/helmets/$1';
For some reason this doesn't work. I borrowed the original code from the blog_routes file and tweaked it to meet the needs of the store module, but I must be missing something. Any clues? BTW, the last line allows my custom button to route to the proper controller method. Thank you for all your help on that!
Erik
What's your module config like? You'll want STORE_FOLDER in your route. Using the members module config from above, the route's I have for that are:
$route[FUEL_ROUTE.'members/members'] = MEMBERS_FOLDER.'/_fuel/users_module'; $route[FUEL_ROUTE.'members/members/(.*)'] = MEMBERS_FOLDER.'/_fuel/users_module/$1'; $route[FUEL_ROUTE.'members/settings'] = MEMBERS_FOLDER.'/_fuel/settings_module'; $route[FUEL_ROUTE.'members/settings/(.*)'] = MEMBERS_FOLDER.'/_fuel/settings_module/$1';
settings_module and users_module are the custom fuel controllers that live in /modules/members/controllers/_fuel/
I've added the _fuel to split the controllers up.