5

I've created a View to search for suitable jobs. Along with View I want to give a "Subscribe" button for those who want matching jobs(if any) be delivered in their inbox daily. I want to save the input entered in the exposed filters and run the View at night in a batch for all those who have subscribed for jobs.

I will add a "Subscribe" button along with "Apply" in the Views form. So if any logged-in user selects the "Subscribe" button I want to: 1) Save the input of exposed filters; 2) Run this view every night and see if any new matches are found; 3) Email the user with new jobs found.

Is there any easy way to do it? Is there any module to help me?

I can use Views Saved Searches module to save current user selection of exposed filters. This input could be fed when calling view programmatically.

Also two more useful links: Using Views Programmatically Drupalcamp & get the result view creates.

EDIT: I need someone to provide me practical in-use implementation that can save my time.

3
  • 1
    a good solution will be to use search api and its contrib drupal.org/project/search_api_saved_searches
    – Ali Nouman
    Dec 16, 2013 at 11:48
  • 1
    If views_savedsearches had support for notifications 7.x you would need just these 2 modules. Dec 23, 2013 at 15:45
  • 1
    I've re-structured your question to reflect the fact that you're really asking 4 questions in one. The title of the original question "Executing a view programmatically with input filters" you already answered with your second link. I think it would be easier to answer your question if it where broken into pieces, as there can be a wild variety of answer to each of the 3 remaining questions you're asking. At this point this isn't as much as a question - as much as a "tell me how to develop a full Drupal application". Hopefully somebody will eventually provide a nice answer ;) Dec 23, 2013 at 23:37

3 Answers 3

3

I don't think that you need to execute the view programmatically. Because it's meaningless. Views just fetches data from the tables which you show and display it, it's all it does. I think you need to alter Exposed Filter form and add your additional submit callback which on submission writes the searched strings into your custom table. And then you can query those table (from which table views fetch data.) with saved string as condition.. All of this logic must be run on cron job..

EDIT: I don't know this can help you or not, but here is a very poor example. Not very performance friendly :). OPTIMIZE IT.

function MY_MODULE_form_views_exposed_form_alter(&$form, &$form_state) {
   if ($form['#id'] == 'MY_EX_FORM_ID') {
      if(user_is_logged_in()) {
         $form['actions'] = array(
            '#type' => 'actions',
         );

         $form['actions']['submit'] = $form['submit'];
         $form['actions']['subscribe'] = array(
            '#type' => 'submit',
            '#value' => 'Subscribe',
            '#submit' => array('my_ex_form_subscribe_submit'),
         );
         unset($form['submit']);
      }
   }
}


function my_ex_form_subscribe_submit($form, &$form_state) {
   global $user;
   $account = clone $user;

   $input_val = $form_state['values']['field_adv_price_first']['min']; //Find your input field value and change this..

   db_insert('MY_SUBSCRIBE_TABLE')
      ->fields(array(
         'uid' => $account->uid,
         'email' => $account->init,
         'input_val' => $input_val,
      ))
      ->execute();
}


function MY_MODULE_cron() {
   $subscribed_query = db_select('MY_SUBSCRIBE_TABLE', 'mst');
   $subscribed_query
      ->fields('mst', array('uid', 'email', 'input_val'))
      ->execute()
      ->fetchAllAssoc('id'/* id - is the primary key of MY_SUBSCRIBE_TABLE */);


   // Supposing that your view is querying for the node table to fetch jobs, by title field
   if(!empty($subscribed_query)) {
      foreach($subscribed_query as $key => $val) {
         $nids = db_select('node', 'n')
            ->fields('n', array('nid'))
            ->condition('title', '%' . db_like($subscribed_query[$key]['input_val']) . '%', 'LIKE')
            ->execute()
            ->fetchCol();

         if(!empty($nids)) {
            foreach($nids as $nid) {
               $node_url = url('node/' . $nid, array('absolute' => TRUE));
               $template = 'New job is found in our site and blablabla... <a href="'. $node_url . '">BLABLABLA</a>';

               // Suppose that you are using SwiftMail library.. You can use drupal's mail system as well. It's up to you
               $transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 465, 'ssl')
                  ->setUsername('MYGMAILUSERNAME')
                  ->setPassword('MYGMAILPASS');

               $mailer = Swift_Mailer::newInstance($transport);

               $message = Swift_Message::newInstance()
                  ->setFrom(array('no-reply@'.$_SERVER['SERVER_NAME'] => 'MY-SITE.com'))
                  ->setTo(array($subscribed_query[$key]['email']))
                  ->setBody($template, 'text/html');
               $mailer->send($message);
            }
         }
      }
   }
}
6
  • Indeed. This is a job for a custom form that just saves the various inputs to be run by a View or other custom sql at some time later on.
    – Jimajamma
    Dec 20, 2013 at 12:49
  • @Jimajamma do you have any sample code which is used at any place?
    – AgA
    Dec 23, 2013 at 10:51
  • @AgA no, sorry, I don't have a current real world use case for this, but it would be pretty straightforward. create a form with the various filters as inputs. display it on a page. when a user submits it, save all the information in a table. then via cron, execute views based upon the info in that table and mail the results to the respective user.
    – Jimajamma
    Dec 23, 2013 at 13:24
  • @Jimajamma plenty of people must be using this feature in this world. Secondly I don't want to run custom sql myself as you've mentioned in first comment. I'd rather execute the View so that it only generates sql automatically
    – AgA
    Dec 23, 2013 at 13:49
  • @AgA I was just responding that I'm sorry, but no, I don't have any sample code at hand that accomplishes this exact task.
    – Jimajamma
    Dec 23, 2013 at 13:51
2

Is there any module to help me?

Much easier to answer if you break down your question logically.

1) Save the input of exposed filters;

The input of exposed filters is relative to the user entities “saving” those filters. In Drupal 7 users are entities, and some entities are fieldable (meaning you can add fields to them). Drupal 7 users are fieldable. This DA question contains all sorts of useful answers you can use to add fields to your user - where later on you can store the exposed filter data: How to add extra fields to user profile?.

2) Run this view every night and see if any new matches are found;

This separate questions gets into batch/cron jobs area. Look into hook_cron. You can define your own Drupal “job” that will run on any schedule you want. When the schedule runs depends if yo use standard system tools from managing cron (cron tab) - or if yo use a Drupal module such as elysia cron.

In your hook_cron() you could write a custom query that would search for all the users with a saved search. Since Drupal 7 users are entities, you can use EntityFieldQuery to get started.

If any users are found, then you could execute the View programatically (as you answered yourself), get the results, and email them. I don’t think Views will provide you with the best e-mail friendly output out of the box. But you could easily write your own custom theming function that parses the output returned by the View. This should be relatively easy for a Drupal beginner to do. Drupal theming is a topic of it's own. So I'm going to spare readers of an answer to that and assume due diligence on behalf of those seeking an answer. Links: one, two.

3) Email the user with new jobs found.

Multiple questions already answered here on DA can help you with that. I’m only providing one, two, three as a sample, but you could keep searching DA to your heart’s content. No reason to re-invent the wheel (or re-answer what’s been already answered).

To conclude - there is no easy answer to your question that I could think of, and my answer would assume that you've got at least a decent handle on Drupal. But I hope it helps nevertheless.

1

In one of my projects I build something like this in custom way.Here are few pointers I can think of now.

  1. Create custom form, custom database table to map filter values with uid,keep a flag_cron column and flag_updated in your custom table,flag_cron which will make sure that you are not picking up same filters to get data for and set flag_updated to 1 every time you get data for a set of filters and again if the user against a uid updates the filters set the flag_updated to 0,So while picking up the filters on cron run(you must be picking up the filters in chunks otherwise when users will grow system will fail to make request for),check for 0,0(flag_cron,flag_udpated) then give priority to 1,0(flag_cron,flag_updated).Key will be every time you get data update flag_cron and flag_udpated to 1. This is what I did in my project, I feel custom forms give more flexibility over expose filters.

  2. On cron run you might be calling API's , so with the fetched data if you are building nodes then views are good solution to create the display while this views is programmatically called with the filter values against the current logged in user or if not set any then you can keep default in place.(creating nodes should be good approch). In case you are not making nodes and simply filling custom tables with data you get,you might make database query and may use different theme function to show the data.

  3. Try to evaluate if better exposed filter form can take place for your custom form,otherwise underlying concept and custom filter table is supposed to be same.

  4. In my projects I have to make several API calls at frequent intervals.Since API calls were large amount and they were fetching large data so I was looking for non blocking system, I used php command line tool exec.Code example

    exec("/usr/bin/php path_to_php_file.php $param_0 $slot_0> /dev/null 2>&1 &");

and $patam $slot are diferent arguments you need on the stanalone php file, the benefit you get using non blocking call system is you can trigger different php process at same time thus making different API calls by passing different arguments in parallel, but its important to control the php process otherwise it may crash your server.Later on I found there is a drupal module which allows to make non blocking calls "backgroung_process"

  1. For cron I used elysia cron and set crontab on server.

Elysia Cron extends Drupal standard cron, allowing a fine grain control over each task and several ways to add custom cron jobs to your site.

Your Answer

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

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