Export Data Option for Custom Modules

edited March 2012 in 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

  • edited 12:03PM
    Easiest way is to use csv_from_result() from CI.

    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(); }
  • edited 12:03PM
    FUEL doesn't have anything built in for backing up a single table. However, you could use the backup feature in the CI database utilities class:
    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')),
  • edited April 2012
    Ok I am almost there with getting this to work, thanks much for the guidance in getting my controller setup. I now have an export function in a controller I've placed in modules/store/controllers/helmets.php.

    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
  • edited 12:03PM
    As you've probably seen the fuel_url() is added on line #61 in FUEL's module_list layout. I'd say the easiest way would be to reroute the FUEL_ROUTE for that link
  • edited April 2012
    Rather than try to re-route that link, however, I would find it more useful to understand how to get a controller function to work with the /fuel included in the URL, that way I understand more how the fuel routing works for when I need to add more custom module controller functions (which is inevitable down the line).

    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.
  • edited April 2012
    Oh I see, sorry I thought you wanted to keep it a simple module.

    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.
  • edited 12:03PM
    Here is an example that may help. Say you have a "search" module that needs a front end controller to display a users search results, and you need a backend controller that displays the administrative end inside FUEL to index your search. In this case you can create 2 controllers... one at fuel/modules/search/controllers/search.php (front end) and fuel/modules/search/controllers/search_module.php (back end). The search_module.php should inherit the Fuel_base_controller.php and a route is already setup by FUEL to be accessed at fuel/search. However, you can add additional routes. Perhaps you actually want it to appear under fuel/tools/search. If so you could add something like the following to handle the controller and methods on that controller:
    $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
  • edited 12:03PM
    Lance, I have it setup exactly how you laid it out, and I can't seem to get that route to quite work also. I'll admit, i'm no expert defining route stuff, but I took this stab at it but it still doesn't work to redirect that fuel URL to my working /store/helmets/export_non_shipped_orders URL:

    $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
  • edited April 2012
    For FUEL to figure that out it'd have to look in every controller/method you may have defined in the applications directory, and also do the same in modules. That's not a solution..

    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';
  • edited 12:03PM
    So yeah I'm still hitting a brick wall with this. This is such as simple thing to do really, all I am really trying to do is point a custom button to a particular URL.

    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?
  • edited April 2012
    Can you link me to a zip of what you have?

    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..
  • edited 12:03PM
    Thank you! This run-down helped me get this to work. My hangup was an error in the URL to which the button was supposed to point. I removed the forward slash before "store" and that got it to work after integrating all the other changes you mentioned:

    $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
  • edited 12:03PM
    Great, glad you got it sorted!

    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.
Sign In or Register to comment.