Page 1 of 4 123 ... LastLast
Results 1 to 10 of 32
  1. #1
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Zencart+Ajax made easy? solution discussion

    I open this thread for anyone who is interesting in implementing Ajax on multiple parts of Zencart, the main goals are:

    1. Implement ajax on current zencart pages/functions without breaking the default behaviors
    2. Allow fall-back method when the browsers do not support javascript
    3. Reduce load time by updating only part of the page (which is one of the main points of ajax anyway)

    Anyhow, here is my approach:
    1. When a page is loaded, check if it is loaded through ajax or not, if it is an ajax request the we use ob_start to store the output
    2. We will then print out only the portion we need.
    3. The javascript on the front-end will take the output and do the updates etc

    Several obstacles so far:
    1. zen_redirect function needs to be replaced
    2. messageStack doesn't store enough information, it is needed to be replaced as well.
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  2. #2
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    Below is the current class I have, it still needs lots of work on it, but currently it provides the most basic functions (and it does work). I post it here for those who may be interested.

    start() needed to be called before anything is echoed out
    end() needed to be called after all output.

    The ideal place to place the 2 functions right now is in the index.php.

    The class will return only the specific blocks of content so for example if you want to update only the left column and the main content you can certainly do so.

    If anyone is interested in this, please feel free to post question and suggestion here. Otherwise, this topic will soon drift to the forgotten land of Narnia and we may not see any decent ajax implementation of zencart in the near future.


    PHP Code:
    <?php

    class Ajax{
        private 
    $status false;
        private 
    $content = array();
        private 
    $return_blocks = array();
        private 
    $messages = array();
        
        public function 
    start(){
            
    // set status
            
    if((!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){
                
    $this->status true;
                
    ob_start();    
            }
        }
        
        public function 
    setOptions($options){
            
    Json::set($options);
        }
        
    //    public function setReturnBlocks($blocks, $structure){
    //        $this->return_blocks = $blocks;
    //        
    //        foreach($structure as $str_key => $str_value){
    //            foreach($blocks as $block_key => $block_value){
    //                if($this->recursiveInArray($block_value, $str_value)){
    //                    unset($blocks[$block_key]);
    //                    $blocks[$str_key] = $str_value;
    //                }
    //            }
    //        }
    //
    //        $this->allow_blocks = $blocks;    
    //    }
        
        
    public function setReturnBlocks($blocks){
            
    $this->return_blocks $blocks;
        }
        
        public function 
    end(){
            global 
    $messageStack;
            
            if(
    $this->status){
                
    ob_end_clean();
                    
                foreach(
    $messageStack->messages as $message){
                    
    Json::addMessage($message['text'], $message['class']);
                }
                
                
    $status Json::get('status');
                if(empty(
    $status))
                    
    $status = isset($this->messages['error']) ? 'error' 'success';
                
                
    Json::setJsonAndExit(array('content' => $this->content'status' => $status));
            }
            else{
                if(
    Json::get('status') == 'resubmit'){
                    
    // use the curl class to resubmit the form
                    
    require_once(DIR_WS_CLASSES.'curl.php');
                    
    $c = new curl(Json::get('action'));        
                    
    $c->setopt(CURLOPT_FOLLOWLOCATIONtrue);
                    
    $c->setopt(CURLOPT_POSTtrue);
                    
    $c->setopt(CURLOPT_POSTFIELDS$c->asPostString($_POST));
                    echo 
    $c->exec();
                    if (
    $theError $c->hasError())
                    {
                      echo 
    $theError;
                    }
                    
    $c->close();
                    exit();
                }
                
    //            foreach($this->content as $c)
    //                echo $c;
            
    }
        }
        
        public function 
    startBlock($block_name){
            if(!
    $this->status || !$this->checkReturnBlock($block_name))
                return 
    false;
                
            
    ob_start();
            return 
    true;
        }
        
        public function 
    endBlock($block_name){
            if(!
    $this->status || !$this->checkReturnBlock($block_name))
                return 
    false;
                
            
    $this->content[$block_name] = ob_get_contents();
            
    ob_end_clean();
            return 
    true;
        }
            
        public function 
    redirect($url$redirect_type null){
            if(
    $this->status){
                
    Json::setJsonAndExit(array('status' => 'redirect''url' => $url,'redirect_type' => $redirect_type'content' => ''));
            }
            else
                
    zen_redirect($url);    
        }
        
        public function 
    getStatus(){
            return 
    $this->status;
        }

        public function 
    getContent(){
            while(
    Json::get('status') == 'resubmit'){
                
            }
        }
        
        public function 
    getMessage(){
            
        }
        
        private function 
    checkReturnBlock($block){
            if(empty(
    $this->return_blocks) || in_array($block$this->return_blocks))
                return 
    true;
            return 
    false;
        }
        
        public function 
    loadLanguage($current_page_base){
            global 
    $language_page_directory$template$template_dir_select;
            
    $directory_array $template->get_template_part($language_page_directory $template_dir_select'/^'.$current_page_base '/');
            while(list (
    $key$value) = each($directory_array)) {
                  echo 
    "I AM LOADING: " $language_page_directory $template_dir_select $value '<br />';
                  require_once(
    $language_page_directory $template_dir_select $value);
            }
              
            
    // load master language file(s) if lang files loaded previously were "overrides" and not masters.
            
    if ($template_dir_select != '') {
                  
    $directory_array $template->get_template_part($language_page_directory'/^'.$current_page_base '/');
                  while(list (
    $key$value) = each($directory_array)) {
                    
    //echo "I AM LOADING MASTER: " . $language_page_directory . $value.'<br />';
                    
    require_once($language_page_directory $value);
                  }
            }
        }
        
        public function 
    addMessage($class$message$type 'error'){
            
    $this->_addMessage($class$message$typefalse);
        }
        
        public function 
    addSessionMessage($class$message$type 'error'){
            
    $this->_addMessage($class$message$typetrue);
        }
        
        private function 
    _addMessage($class$message$type 'error'$session false){
            global 
    $messageStack;
            
    $messageStack->add_session($class$message$type);
            
    $this->messages[$type] = array('class' => $class'message' => $message'session' => $session);
        }
    //    
    //    private function recursiveInArray($needle, $haystack) {
    //        foreach ($haystack as $key => $stalk) {
    //            if (($key === $needle) || ($needle === $stalk) || (is_array($stalk) && recursive_in_array($needle, $stalk))) {
    //                return true;
    //            }
    //        }
    //        return false;
    //    }
    }
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  3. #3
    Join Date
    Sep 2008
    Location
    Toronto
    Posts
    35
    Plugin Contributions
    0

    Default Re: Zencart+Ajax made easy? solution discussion

    i was looking for a ZenCart Ajax search script, where it will suggest some keyworks for the customer real time when writing the search term.

    do you guys know any technology similar to the one i'm taking about that can be used in zencart?

    thanks.

  4. #4
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    Quote Originally Posted by hovsepations View Post
    i was looking for a ZenCart Ajax search script, where it will suggest some keyworks for the customer real time when writing the search term.

    do you guys know any technology similar to the one i'm taking about that can be used in zencart?

    thanks.
    Probably opening another thread for this topic would be better.

    Anyway, back to the main topic, I want to thanks several people who pmed me for the encouragement, it really helps.

    Some of you asked if I use any specific. At the moment jquery is my favorite one, it's so simple because it's so close to css, it's so easy for me to learn (I used to struggle with dojo and mootools)

    Some of you asked about the Json class I used, so here it is. Note that it is written for php5, but we can easily rewrite it for php4

    PHP Code:
    <?php
    class Json{
        static private 
    $json = array();
        
        public static function 
    reset(){
            
    self::$json = array();
        }
        
        public static function 
    set($data){
            
    self::$json array_merge_recursive(self::$json$data);
        }
        
        public static function 
    get($key null){
            if(!empty(
    $key))
                return 
    self::_get(self::$json$key);
            else 
                return 
    self::$json;
        }
        
        private static function 
    _get($data$key){
            if(
    is_array($key)){
                
    $sub_key array_shift($key);
                
                if(isset(
    $data[$sub_key])){
                    if(
    count($key) > 0)
                        return 
    self::_get($data[$sub_key], $key);
                    else 
                        return  
    $data[$sub_key];
                }
                else 
                    return 
    $data;
            }
            else {
                if(isset(
    $data[$key]))
                    return  
    $data[$key];
                else 
                    return 
    null;
            }
        }
        
        public static function 
    add($key$content){
            
    self::$json[$key] .= $content;
        }
        
        
    // This function is to replace Zen's messageStack. Basically we return the messages to the client, the JS will deal with them.
        
    public static function addMessage($message$type='error'){
            
    self::add('message'"<div class='$type'>$message</div>");
        }
        
        public static function 
    getJson(){
            return 
    json_encode(self::$json);    
        }
        
        public static function 
    setJsonAndExit($data){
            
    self::set($data);
            echo 
    self::getJson();
            exit();
        }

        public static function 
    getJsonAndExit(){
            echo 
    self::getJson();
            exit();
        }
    }
    Next time I will post something regarding my current progress with coding an Ajax checkout page for zencart. Obviously my approach is not the best approach (Im still an Ajax newbie), but hopefully together we can discuss and come up with a good solution for using ajax in zencart.
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  5. #5
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    Two of the main concerns I have when working on ajax is:
    1. What if the user disables js?
    2. How to avoid loading unnecessary parts when loading page with ajax?

    For the first part, I get around this by first determining if the request is an ajax call (for now, by checking the http header). If the request is an ajax call, then we will start to turn on output buffer and so the normal output will be off. Otherwise, we will just output content as normal.
    (check the start() and end() methods)

    This actually works pretty well when I work on the checkout_shipping page for example, correct output is returned based on the request type.

    But there are a few things in the core files I had to make changes:
    a. the zen_redirect. This function will have no effect on ajax call, so we need to replace it with our own redirect function, which will do the proper redirection based on the request type.(check redirect method)
    b. the messageStack methods: the messages stored in this object really doesnt help much to determine the type of message, and parsing the text string inside to determine the message type is really unreliable. So we have to replace that with our message functions as well. (check addMessage and addSessionMessage methods)


    Now lets move to point 2: how do we return proper portion of the output we want, and avoid running the parts we dont want (for example, if I request the checkout_shipping page via ajax, I dont need header, footer, left column, right column. I need only the main content)

    My current solution is to break the output into blocks (we use startBlock() and endBlock() methods in the template files to set this), and we set the blocks we want to return. Obviously, in the future we may want to allow the "return blocks" to be specified in the request (get/post) as well.

    Now that we already set the blocks we want to return, it's rather obvious that we want to ignore the other blocks and simply not processing any code in those blocks at all (when we use ajax request), it gets a bit difficult here, since blocks can be nested. For example, if my mainContent block contains the leftColumn block, then obviously even if I only want to get the leftColumn I still have to process the whole mainContent. At the same time, I can avoid processing any code in the rightColumn which is also in the mainCotent though, I'm thinking of a way to do this, probably the "block structure" have to be declared fist, otherwise we have no way to know if a block is nested in another block.


    That's it for now. I hope you are enjoying this. I will post more regarding the process later, but I will concentrate on the server side part for now, we will get to the client side part later.
    Last edited by yellow1912; 3 Dec 2008 at 10:54 AM.
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  6. #6
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    My rant on this topic continues....(alone)

    Today is about the blocks to avoid reading, I think ultimately we can do something like this
    PHP Code:
    if($Ajax->startBlock("blockName")){

    // code goes here

    $Ajax->endBlock();

    The code in the class above will have to be changed slightly, but it's relative easy, the hardest part would be the nested blocks, im thinking that we have to pass the block structure in like this

    PHP Code:
     array('header','body'=>array('leftCoiumn'=>array('sideboxe1','sidebox2'),'rightColumn','mainContent'=>array('mainContent1','mainContent2')) 
    And then each time we encounter a block, we will have to see if that block is in the "return" list, or if that block contains a block in the "return" list.
    Maybe there is a faster way?
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  7. #7
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    Today I will rant about loading js scripts into pages, this sounds like a simple task but it can give you a headache.

    Lets say we use jquery library, this one has a large number of plugins. We do not want to load jquery or the plugins on any page, we want to load them on conditions (depending on the current page or any other condition), and we usually want to load only several of those depend on the specific need.
    To make matter worse, if we have several ajax plugins for zencart using jquery, if each of them tries to load the same scripts then we will run into unexpected problems.

    Here is my (proposed) approach to it:
    We will define a global var like this in the extra_datafiles
    PHP Code:
        $jscripts[] = array('jquery.js' => array('path' => 'jquery/',
                                                
    'conditions' => array('pages' => array('product_info''login'),
                                                                      
    'call_backs' => array('is_category'))),
                          
    'form.jquery.js' => array('path' => 'jquery/',
                                                
    'conditions' => array('pages' => array('login'),
                                                                      ))); 
    (call_backs are call-back methods which return true/false to decide if the script should be loaded)

    Now if you have a new ajax plugin for zencart that needs to load the jquery.js on pages product_info and contact_us, you can simply create a new file in extra_datafiles with this content:
    PHP Code:
    $jscripts[] = array('jquery.js' => array(
                                                
    'conditions' => array('pages' => array('product_info''contact_us'),
                                                                      ))); 
    This $jscripts array will be merged recursively, all duplicated instances will be removed, and the scripts will be loaded properly.
    We can also add an 'order' or 'index' key to make sure the scripts are loaded in the order we want.

    Anyhow, this approach will let us includes the js scripts seamlessly while avoiding conflicts among different ajax modules.
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  8. #8
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    Okie, here is the code to get the result Im talking about above.

    It's still kinda messy but it does work and it's just for the purpose of demonstrating the algorithm.

    PHP Code:
    <?php
                                                                      
    $jfiles 
    = array();
    foreach(
    $jscripts as $j){
        
    $jfiles array_merge_recursive($jfiles$j);
    }

    $js_queue = array();

    foreach(
    $jfiles as $file_name => $options){
        
    $js_file '';
        if(
    in_array($current_page_base$options['conditions']['pages']))
            
    $js_file get_js_path($file_name$options['path']);
        else{
            
    $load false;
            
            foreach(
    $options['conditions']['call_backs'] as $function){
                
    $f explode(',',$function);
                if(
    count($f) == 2){
                    
    $load call_user_func(array($f[0], $f[1]));
                }
                else 
    $load $function();
                
                if(
    $load){
                    
    $js_file get_js_path($file_name$options['path']);
                    break;
                }
            }
        }
        if(!empty(
    $js_file)){
            if(isset(
    $options['order']))
                
    $js_queue[$options['order']][] = $js_file;    
            else 
                
    $js_queue[] = $js_file;
        }
        
    }


    if(
    count($js_queue) > 0){
        
    sort($js_queue);
        foreach(
    $js_queue as $file_to_load){
            if(
    is_array($file_to_load)){
                foreach(
    $file_to_load as $file)
                    echo 
    "<script type='text/javascript' src='$file'></script>\n";
            }
            else
                echo 
    "<script type='text/javascript' src='$file_to_load'></script>\n";
        }
    }

    function 
    get_js_path($file_name$path){
        return 
    DIR_WS_TEMPLATE.'jscript/'.$path.$file_name;
    }

    function 
    is_category(){
        return (isset(
    $_GET['main_page']) && isset($_GET['cPath']) && $_GET['main_page'] == 'index');
    }
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  9. #9
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    New code to take care of .php javascript file

    PHP Code:
    $jfiles = array();
    foreach(
    $jscripts as $j){
        
    $jfiles array_merge_recursive($jfiles$j);
    }

    $js_queue = array();

    foreach(
    $jfiles as $file_name => $options){
        
    $js_file '';
        if(
    in_array($current_page_base$options['conditions']['pages']))
            
    $js_file get_js_path($file_name$options['path']);
        else{
            
    $load false;
            
            foreach(
    $options['conditions']['call_backs'] as $function){
                
    $f explode(',',$function);
                if(
    count($f) == 2){
                    
    $load call_user_func(array($f[0], $f[1]));
                }
                else 
    $load $function();
                
                if(
    $load){
                    
    $js_file get_js_path($file_name$options['path']);
                    break;
                }
            }
        }
        if(!empty(
    $js_file)){
            if(isset(
    $options['order']))
                
    $js_queue[$options['order']][] = $js_file;    
            else 
                
    $js_queue[] = $js_file;
        }
    }


    if(
    count($js_queue) > 0){
        
    sort($js_queue);
        foreach(
    $js_queue as $file_to_load){
            if(
    is_array($file_to_load)){
                foreach(
    $file_to_load as $file)
                    
    include_js($file);
            }
            else
                
    include_js($file_to_load);
        }
    }

    function 
    get_js_path($file_name$path){
        
    $path_info pathinfo($file_name);
        return array(
    'extension' => $path_info['extension'], 'path' => DIR_WS_TEMPLATE.'jscript/'.$path.$file_name);
    }

    function 
    include_js($file){
        if(
    $file['extension'] == 'js')
            echo 
    "<script type='text/javascript' src='{$file['path']}'></script>\n";
        elseif(
    $file['extension'] == 'php')
            include(
    $file['path']);

    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

  10. #10
    Join Date
    Oct 2006
    Posts
    5,477
    Plugin Contributions
    11

    Default Re: Zencart+Ajax made easy? solution discussion

    I spent many hours today to fix several bugs and make sure things work as expected.

    Here are several functions needed to load the js correctly, as well as some demo call_backs function. Im thinking of moving these call_back functions into an extensible class though, to avoid possible conflict and to let people reuse the functions easily.

    PHP Code:
    <?php
        
    function array_merge_recursive2($array1$array2)
    {
        
    $arrays func_get_args();
        
    $narrays count($arrays);
       
        
    // check arguments
        // comment out if more performance is necessary (in this case the foreach loop will trigger a warning if the argument is not an array)
        
    for ($i 0$i $narrays$i ++) {
            if (!
    is_array($arrays[$i])) {
                
    // also array_merge_recursive returns nothing in this case
                
    trigger_error('Argument #' . ($i+1) . ' is not an array - trying to merge array with scalar! Returning null!'E_USER_WARNING);
                return;
            }
        }
       
        
    // the first array is in the output set in every case
        
    $ret $arrays[0];
       
        
    // merege $ret with the remaining arrays
        
    for ($i 1$i $narrays$i ++) {
            foreach (
    $arrays[$i] as $key => $value) {
                if (((string) 
    $key) === ((string) intval($key))) { // integer or string as integer key - append
                    
    $ret[] = $value;
                }
                else { 
    // string key - megre
                    
    if (is_array($value) && isset($ret[$key])) {
                        
    // if $ret[$key] is not an array you try to merge an scalar value with an array - the result is not defined (incompatible arrays)
                        // in this case the call will trigger an E_USER_WARNING and the $ret[$key] will be null.
                        
    $ret[$key] = array_merge_recursive2($ret[$key], $value);
                    }
                    else {
                        
    $ret[$key] = $value;
                    }
                }
            }   
        }
       
        return 
    $ret;
    }


    function 
    getJsPath($file_name$path){
        
    $path_info pathinfo($file_name);
        return array(
    'extension' => $path_info['extension'], 'path' => DIR_WS_TEMPLATE.'jscript/'.$path.$file_name);
    }

    function 
    includeJs($file){
        if(
    $file['extension'] == 'js')
            echo 
    "<script type='text/javascript' src='{$file['path']}'></script>\n";
        elseif(
    $file['extension'] == 'php')
            include(
    $file['path']);
    }

    function 
    isLoggedIn(){
        return (isset(
    $_SESSION['customer_id']) && (int)$_SESSION['customer_id'] > 0);
    }

    function 
    isNotLoggedIn(){
        return !
    isLoggedIn();
    }
    I no longer provide installation support on forum for all my modules. However, if there are real bugs with the modules please feel free to contact me

 

 
Page 1 of 4 123 ... LastLast

Similar Threads

  1. v151 Fast and easy ajax checkout
    By westnilewonder in forum All Other Contributions/Addons
    Replies: 1
    Last Post: 5 Mar 2015, 07:12 PM
  2. v151 Ajax Fast & Easy Checkout
    By fsb4e in forum General Questions
    Replies: 1
    Last Post: 30 Jun 2014, 06:12 PM
  3. My solution for incompatibility between Ceon URI mapping and Ajax Swapper Image
    By mybiz9999 in forum All Other Contributions/Addons
    Replies: 0
    Last Post: 4 Aug 2010, 07:56 AM
  4. Ajax Site Preload (easy Interigation)?
    By lilmikey in forum General Questions
    Replies: 0
    Last Post: 3 Jul 2008, 05:06 PM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
disjunctive-egg
Zen-Cart, Internet Selling Services, Klamath Falls, OR