theoleschool - stockAPI http://theoleschool.com/tags/stockapi en Quick Stock Quote on Page Refresh http://theoleschool.com/blog/quick-stock-quote-page-refresh-0 <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>I thought I would throw a post up, demonstrating a "quick and easy" use case of the <a href="http://drupal.org/project/stockapi">Stock API module</a>, and simultaneously, give this site's <a href="http://qbnz.com/highlighter/">GeSHi filter</a> a quick test run.</p> <p>A few days ago I encountered a site requirement asking for a simple informative stock quote block on a Drupal 6.x installation. The format required was something like: <br /><em>NFLX 208.75 -1.30 Sep 13 7:30 EST</em> <br /> My initial thought was to enable the <a href="http://drupal.org/project/stock">Stock module</a> and simply configure and theme the built in block. The stock module will allow the management of multiple stocks on a per-user basis, and provides a block that displays this information in a table. There is no doubt that stock module is useful but in this instance it is overkill. All we are after is a simple quote from a single unchanging stock symbol displayed in a single line of text.</p> <p>So let's take a short step back to the stock API module. There is really one "go to" function when grabbing a quote from the stock api and that is <span class="geshifilter"><code class="php geshifilter-php">stockapi_load<span style="color: #009900;">(</span><span style="color: #000088;">$symbol</span><span style="color: #009900;">)</span></code></span>. This function is defined like so:</p> <div class="geshifilter"><div class="drupal6 geshifilter-drupal6" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> stockapi_load<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">(</span><span style="color: #66cc66;">!</span><a href="http://www.php.net/empty"><span style="color: #000066;">empty</span></a><span style="color: #66cc66;">(</span><span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />     <span style="color: #0000ff;">$stock</span> = <a href="http://api.drupal.org/api/function/db_fetch_array/6"><span style="color: #000066;">db_fetch_array</span></a><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/db_query/6"><span style="color: #000066;">db_query</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">"SELECT * FROM {stockapi} WHERE symbol = '%s'"</span>, <span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>;<br />     <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">(</span><span style="color: #66cc66;">!</span><span style="color: #0000ff;">$stock</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />       <span style="color: #0000ff;">$stock</span> = stockapi_fetch<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span>;<br />       <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">(</span><span style="color: #0000ff;">$stock</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />         <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">(</span><span style="color: #0000ff;">$stock</span><span style="color: #66cc66;">[</span><span style="color: #cc66cc;">8</span><span style="color: #66cc66;">]</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff0000;">'N/A'</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />           <span style="color: #808080; font-style: italic;">// Date is 'N/A' means an invalid stock symbol</span><br />           stockapi_save<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$stock</span><span style="color: #66cc66;">)</span>;<br /><br />           <span style="color: #808080; font-style: italic;">// Load again so it can show for the first time</span><br />           <span style="color: #0000ff;">$stock</span> = stockapi_load<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span>;<br />         <span style="color: #66cc66;">}</span><br />       <span style="color: #66cc66;">}</span><br />     <span style="color: #66cc66;">}</span><br />   <span style="color: #66cc66;">}</span><br />   <span style="color: #b1b100;">return</span> <span style="color: #0000ff;">$stock</span>;<br /><span style="color: #66cc66;">}</span></div></div> <p>As you can see the stock API module is wanting to store stock information in the database to be updated on cron runs. It does this as a type of database caching. This way if a site is pulling information on many stocks, potentially every page load, compounded by many users the API can retrieve the information with a simple query to the local database. Not only is this method much faster than hitting the third party service (yahoo) every time, it is able to serve saved information (especially handy if that service happens to be down or a connection cannot be established). This feature along with returning a nice keyed array of stock information is reason enough to love this function. However, I think we could go a bit simpler still. We only need information on one stock site wide, and in this instance that information should be as current as possible. Furthermore, I would like to avoid setting up a cron job to run every half hour just to keep stock information current. Rather, we look further into the Stock API and make a more basal call to <span class="geshifilter"><code class="php geshifilter-php">stockapi_fetch<span style="color: #009900;">(</span><span style="color: #000088;">$symbol</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></code></span>. This function is a direct call to the 3rd party service, in this case yahoo:</p> <div class="geshifilter"><div class="drupal6 geshifilter-drupal6" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/**<br />  * Fetch the stock info for a given ticker symbol from Yahoo!<br />  */</span><br /><span style="color: #000000; font-weight: bold;">function</span> stockapi_fetch<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">(</span><a href="http://www.php.net/empty"><span style="color: #000066;">empty</span></a><span style="color: #66cc66;">(</span><span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />     <span style="color: #b1b100;">return</span> <span style="color: #000000; font-weight: bold;">FALSE</span>;<br />   <span style="color: #66cc66;">}</span><br /><br />   <span style="color: #0000ff;">$fields</span> = <span style="color: #ff0000;">'snl1c1ohgvd1t1'</span>;<br />   <span style="color: #808080; font-style: italic;">/*<br />     "s"  =&gt; "Symbol",<br />     "n"  =&gt; "Name",<br />     "l1" =&gt; "Last",<br />     "c1" =&gt; "Change",<br />     "o"  =&gt; "Opening",<br />     "h"  =&gt; "High",<br />     "g"  =&gt; "Low",<br />     "v"  =&gt; "Volume",<br />     "d1" =&gt; "Date",<br />     "t1" =&gt; "Time"<br />    */</span><br /><br />   <span style="color: #0000ff;">$host</span> = <span style="color: #ff0000;">'http://download.finance.yahoo.com'</span>;<br />   <span style="color: #0000ff;">$url</span> = <span style="color: #0000ff;">$host</span> .<span style="color: #ff0000;">'/d/quotes.csv?s='</span>. <a href="http://www.php.net/urlencode"><span style="color: #000066;">urlencode</span></a><span style="color: #66cc66;">(</span><span style="color: #0000ff;">$symbol</span><span style="color: #66cc66;">)</span> .<span style="color: #ff0000;">'&amp;f='</span>.<span style="color: #0000ff;">$fields</span>.<span style="color: #ff0000;">'&amp;e=.csv'</span>;<br /><br />   <span style="color: #0000ff;">$result</span> = <a href="http://api.drupal.org/api/function/drupal_http_request/6"><span style="color: #000066;">drupal_http_request</span></a><span style="color: #66cc66;">(</span><span style="color: #0000ff;">$url</span><span style="color: #66cc66;">)</span>;<br />   ...</div></div> <p>This ensures fresh data, without a cron run to update the database. This function is not quite as nice as the <span class="geshifilter"><code class="php geshifilter-php">stockapi_load<span style="color: #009900;">(</span><span style="color: #009900;">)</span></code></span> function in that the array it returns is not keyed. The commented section above (from the module) comes to the rescue. It is in the same sequence as the array returned so it can be used as a reference. So to return something like our requirement example above:<br /><em>NFLX 208.75 -1.30 Sep 13 7:30 EST</em><br /> We would write something like:</p> <div class="geshifilter"><div class="drupal6 geshifilter-drupal6" style="font-family:monospace;"><span style="color: #0000ff;">$output</span> = <span style="color: #ff0000;">''</span>;<br /><span style="color: #0000ff;">$my_stock</span> = stockapi_fetch<span style="color: #66cc66;">(</span><span style="color: #ff0000;">'NFLX'</span><span style="color: #66cc66;">)</span>;<br /><span style="color: #b1b100;">if</span><span style="color: #66cc66;">(</span><span style="color: #0000ff;">$my_stock</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <span style="color: #0000ff;">$output</span> .= <span style="color: #ff0000;">'&lt;span class="my-stock-quote"&gt;'</span>.<span style="color: #0000ff;">$my_stock</span><span style="color: #66cc66;">[</span><span style="color: #cc66cc;">0</span><span style="color: #66cc66;">]</span>.<span style="color: #ff0000;">' '</span>.<span style="color: #0000ff;">$my_stock</span><span style="color: #66cc66;">[</span><span style="color: #cc66cc;">2</span><span style="color: #66cc66;">]</span>.<span style="color: #ff0000;">' '</span>.<span style="color: #0000ff;">$my_stock</span><span style="color: #66cc66;">[</span><span style="color: #cc66cc;">3</span><span style="color: #66cc66;">]</span>.<span style="color: #ff0000;">' '</span>.<span style="color: #0000ff;">$my_stock</span><span style="color: #66cc66;">[</span><span style="color: #cc66cc;">8</span><span style="color: #66cc66;">]</span>.<span style="color: #ff0000;">' '</span>.<span style="color: #0000ff;">$my_stock</span><span style="color: #66cc66;">[</span><span style="color: #cc66cc;">9</span><span style="color: #66cc66;">]</span>.<span style="color: #ff0000;">' EST&lt;/span&gt;'</span>;<br /><span style="color: #66cc66;">}</span><br /><span style="color: #b1b100;">return</span> <span style="color: #0000ff;">$output</span>;</div></div> <p>I wound up throwing a variant of the snippet above into a block created in my module, allowing me to then jump to panels and throw the output wherever I wanted.</p> <p>Now I know... the example is somewhat contrived. There is no "secret" here. Everything outlined in this post would quickly be revealed by only a few minutes of looking through the Stock API module. I wanted to demonstrate that by making a simple call to the Stock API I was able to save significant time in themeing, as well as gain flexibility in function. The alternative would be wrangling the output of the Stock module's block, and making concessions for it's functional behavior. It is easy to fall into the thinking that Drupal site building is just finding a module that is a closest fit and wrestling with what it gives us. Indeed sometimes that may be the case (Organic Groups comes to mind), but to follow that line of thinking blindly is most certainly a mistake.</p></div></div></div> Fri, 16 Sep 2011 04:28:56 +0000 rho 154 at http://theoleschool.com