#1
  1. A Change of Season
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Mar 2004
    Location
    Next Door
    Posts
    3,558
    Rep Power
    221

    Does looping inside arrays and calling functions chew memory? Api endpoint testing?


    Hi;

    Some users have +50 million contacts on their list. A common theme is working with prospects and dates.

    Here is the code:

    PHP Code:
    foreach($this->data['plcs'] as $val=>$row)
        {
            
    $this->data['plcs'][$count]['currently_here']=0;
            
    $here=array();
            
    $this->data['plcs'][$count]['here'][]="";
            if(isset(
    $this->data['launch_prospects']))
              {
                foreach(
    $this->data['launch_prospects'] as $v=>$k)
                  {
                    
    $datetime2 date_create(date('Y-m-d'));
                    
    $datetime1 date_create($k['date_added']);
                    
    $interval date_diff($datetime1$datetime2);
                    
    $days_prospect_in_sequence $interval->format('%R%a');
                    if(
    $days_prospect_in_sequence >= $row['from'] && $days_prospect_in_sequence $row['to'])
                      {
                        
    $this->data['plcs'][$count]['here'][]=$k['PID'];
                        
    $this->data['plcs'][$count]['currently_here']++;
                      }
                  } 
              }
            
    $count++;  
        } 
    $this->data['plcs'] is a tiny array with maximum 4 elements. However, we got relatively large prospects list and some date functions.

    Here are my questions:

    1 - Does this way of using methods chew resources?
    PHP Code:
    $datetime2 date_create(date('Y-m-d'));
    $datetime1 date_create($k['date_added']);
    $interval date_diff($datetime1$datetime2);
    $days_prospect_in_sequence $interval->format('%R%a'); 
    Would it be a better idea to do this?
    PHP Code:
    $days_prospect_in_sequence time() - strtotime($k['date_added']);
    $days_prospect_in_sequence floor($days_prospect_in_sequence / (60 60 24)); 
    2 - As in the API endpoint. This is what they get. I need to test and see how to handle large data received simultaneously.

    The ping URL with webhooks, and pass lists of +5 million records.

    How can I test this under pressure? I need to generate X number of emails and make the POSTS? I found this but not sure how to generate emails.

    This is my endpoint:

    PHP Code:
    <?php
    defined
    ('BASEPATH') OR exit('No direct script access allowed');

    class 
    Data_receiver extends CI_Controller {

        public function 
    __construct()
            {
                
    parent::__construct();
            }

        public function 
    index()
            {
                
    date_default_timezone_set('Australia/Brisbane');
                
    //$this->output->enable_profiler(TRUE);
                
    $this->db->trans_start();
                
    //Validate Account Owner
                
    $sql "SELECT * FROM launch_owners WHERE id = ?";
                
    $query $this->db->query($sql, array($_POST['user_id']));
                if(
    $query->num_rows()!=1)
                    {
                        
    //echo "Invalid Request";exit();
                    
    }
                else
                    {
                        
    $results $query->result_array()[0];    
                        
    //More Validate Account Owner
                        
    if($results['status']!='active' || $results['secret_key']!=$_POST['secret_key'])
                            {
                                
    //echo "Inactive Account Or Invalid Secret";exit();
                            
    }
                        else 
                            {
                                
    //Validate Launch
                                
    $sql "SELECT * FROM launch_launches WHERE id = ? AND launch_type = ?";
                                
    $query $this->db->query($sql, array($_POST['launch_id'], 'evergreen'));
                                if(
    $query->num_rows()==1)
                                    {
                                        
    //Does Prospect exist under this user?
                                        
    $sql "SELECT * FROM launch_prospects WHERE email = ? AND owner_id = ?";
                                        
    $query $this->db->query($sql, array($_POST['prospect_email'], $_POST['user_id']));
                                        if(
    $query->num_rows()==1)
                                            {
                                                
    $prospect_id $query->result_array()[0]['id'];
                                            }
                                        else 
                                            {
                                                
    $data = array(
                                                    
    'email' => $_POST['prospect_email'],
                                                    
    'owner_id' => $_POST['user_id'],
                                                );
                                                
    $this->db->insert('launch_prospects'$data);
                                                
    $prospect_id $this->db->insert_id();
                                            }
                                        
    //Delete prospect from this launch if already exist in the seqeunce
                                        
    $this->db->delete('launch_launch_prospect', array('launch_id' => $_POST['launch_id'], 'prospect_id'=>$prospect_id)); 
                                        
    //Add Prospect To Launch
                                        
    $data = array(
                                                    
    'launch_id' => $_POST['launch_id'],
                                                    
    'prospect_id' => $prospect_id,
                                                    
    'time_added' => time(),
                                                    
    'date_added' => date('Y-m-d')
                                            );
                                        
    $this->db->insert('launch_launch_prospect'$data);

                                        
    //Insert the same into stats table
                                        
    $data = array(
                                                    
    'launch_id' => $_POST['launch_id'],
                                                    
    'prospect_id' => $prospect_id,
                                                    
    'time_added' => time(),
                                                    
    'date_added' => date('Y-m-d'),
                                                    
    'source' => $_POST['source']
                                            );
                                        
    $this->db->insert('launch_launch_prospect_history'$data);
                                        
    //echo "Added Successfully";
                                    
    }
                                else 
                                    {
                                        
    //echo "Invalid launch";exit();
                                    
    }    
                            }    
                    }    



            
    $this->db->trans_complete();        

            }
    }
  2. #2
  3. Backwards Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,893
    Rep Power
    9646
    Originally Posted by English Breakfast Tea
    1 - Does this way of using methods chew resources?
    Stuff only uses resources while it sits in memory. $datetime2, $datetime1, and $interval are all in memory and will require resources, but when they're no longer needed - which is when the function returns and variables are cleaned up - the memory will be reclaimed. Overwriting values will recover the memory from the old value and take memory for the new value, but the values are the same types (DateTime, DateTime, and DateInterval) then there won't be a change in overall usage.

    All in all it will use a tiny bit more memory, but in exchange you get accurate date calculations so it's worth it. Don't do your own date math.

    data[plcs] may be small but what about [launch_prospects]?

    Originally Posted by English Breakfast Tea
    How can I test this under pressure? I need to generate X number of emails and make the POSTS?
    This is the realm of software testing. It's time for you to learn testing techniques.

    Look at your Data_receiver. It's a regular controller and performs all its controller actions, but it very tightly couples the information source (real data from the database) to how the data is used (all that processing). If you can decouple them such that you can provide your own data to be processed then you can take the next step of generating fake data and handing it to the same processing code.

    Remember dependency injection? This is a place for it. Create a class or two to use in places. Then combine it with mocking, where you create fake classes that emulate real behavior.

    1. Move all this code to a new class #1 that is independant of the controller.
    2. Examine your code to see where it needs to interact with the database. Both for reading and writing.
    3. Move all that stuff off into its own class #2C with one method for each operation. It must be governed by an interface #2I. This class should have all the information necessary to do its job, short of whatever is calculated by the processing code.
    4. Replace that now-removed behavior with calls to this class. This requires the processing code is given an instance of the interface - not the class itself, and definitely not static calls to this class.
    5. Since your controller should be almost entirely empty now, add code to (a) create an instance of the #2C class and (b) give it to an instance of the #1 class.

    That brings you back to where you were with a functioning API. Which means you need to test to make sure that it works before continuing.

    6. Create a mock class #3 that follows the #2I interface.
    7. Fill this class with code that does the testing you need. It can do it however you want provided that it looks and acts like the original #2C class.
    8. When you want to test, write code to (a) create an instance of the #3 class and (b) give it to an instance of the #1 class. Like the controller does.


    The problem with it all is that I'm suggesting you make some significant changes to your application. The biggest one is that you stop doing database queries all over the place. Like, all the stuff about launch owners and launches and prospects should be in their own classes. Doing that isn't much better architecture but makes it much easier to do the kind of legitimate testing you want to do.
    How much code do you have about all this?
  4. #3
  5. A Change of Season
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Mar 2004
    Location
    Next Door
    Posts
    3,558
    Rep Power
    221
    Originally Posted by requinix
    Stuff only uses resources while it sits in memory. $datetime2, $datetime1, and $interval are all in memory and will require resources, but when they're no longer needed - which is when the function returns and variables are cleaned up - the memory will be reclaimed. Overwriting values will recover the memory from the old value and take memory for the new value, but the values are the same types (DateTime, DateTime, and DateInterval) then there won't be a change in overall usage.

    All in all it will use a tiny bit more memory, but in exchange, you get accurate date calculations so it's worth it. Don't do your own date math.

    data[plcs] may be small but what about [launch_prospects]?


    This is the realm of software testing. It's time for you to learn testing techniques.

    Look at your Data_receiver. It's a regular controller and performs all its controller actions, but it very tightly couples the information source (real data from the database) to how the data is used (all that processing). If you can decouple them such that you can provide your own data to be processed then you can take the next step of generating fake data and handing it to the same processing code.

    Remember dependency injection? This is a place for it. Create a class or two to use in places. Then combine it with mocking, where you create fake classes that emulate real behavior.

    1. Move all this code to a new class #1 that is independant of the controller.
    2. Examine your code to see where it needs to interact with the database. Both for reading and writing.
    3. Move all that stuff off into its own class #2C with one method for each operation. It must be governed by an interface #2I. This class should have all the information necessary to do its job, short of whatever is calculated by the processing code.
    4. Replace that now-removed behavior with calls to this class. This requires the processing code is given an instance of the interface - not the class itself, and definitely not static calls to this class.
    5. Since your controller should be almost entirely empty now, add code to (a) create an instance of the #2C class and (b) give it to an instance of the #1 class.

    That brings you back to where you were with a functioning API. Which means you need to test to make sure that it works before continuing.

    6. Create a mock class #3 that follows the #2I interface.
    7. Fill this class with code that does the testing you need. It can do it however you want to be provided that it looks and acts like the original #2C class.
    8. When you want to test, write code to (a) create an instance of the #3 class and (b) give it to an instance of the #1 class. Like the controller does.


    The problem with it all is that I'm suggesting you make some significant changes to your application. The biggest one is that you stop doing database queries all over the place. Like, all the stuff about launch owners and launches and prospects should be in their own classes. Doing that isn't much better architecture but makes it much easier to do the kind of legitimate testing you want to do.
    How much code do you have about all this?
    Hi;


    About functioning API

    I read your comments about dependency injection and updating that code. 2 things:

    1 - I will probably struggle and spam the forum to achieve what you suggested.
    2 - I am not sure if it's worth doing it only for the purpose of testing. I mean let's forget beauty.
    Is the code robust? All that matters to me is robust and functioning.
    If that ugly code is capable of dealing with millions of records, for me it's good enough.

    As in testing, I thought I test it with one of these. Do you have any recommendations?

    About how much code I have

    I am not sure what determines the size of the project. Trying to think if I have much code or not.

    Here I can explain it this way:

    I have 1 end point that receives data from other apps. The Data_receiver class you saw.

    The rest of the app is really only ok.

    The structure is really simple. Half MVC with CI, like hybrid OOP and procedural.

    The way I do projetcs, is that first I make a "good enough version: working, then I clean it as much as I can.

    Cleaning up, I'll end up with code like this:
    PHP Code:
    public function can_prospect_see_plc($plc_id$prospect_email)
      {
        
    $CI =& get_instance();
        
    $CI->custom_functions->set_timezone_by_plc_id($plc_id);
        
    $CI->plc_library->is_valid_plc($plc_id);  
        
    $launch_id $CI->plc_library->launch_id($plc_id);
        
    $prospects_id $CI->prospects_library->prospect_id_by_email($prospect_email);
        
    $open_sales_page_x_days_after_last_plc $CI->launch_library->open_sales_page_x_days_after_last_plc($launch_id);
        
    $how_many_days_keep_sales_page_open $CI->launch_library->how_many_days_keep_sales_page_open($launch_id);
        
    $CI->plc_library->releases_this_plc_after_x_days($plc_id);
        
    $days_in_launch $CI->prospects_library->how_many_days_propspect_been_in_launch($prospects_id$launch_id);
      } 
    What do I mean by "good enough"...===> No sql in loops, bind neccessary queries, escape all user inout data and not much php in views.

    I have a few libraries, and quite a few duplicate code. That can be improved and fixed. That's my plan for next week.

    I got about 30 controllers, no models, 10 libraries and 100 views.

    Here are some code samples:

    A clean controller
    PHP Code:
    <?php
    defined
    ('BASEPATH') OR exit('No direct script access allowed');

    class 
    Account extends CI_Controller {

        public function 
    __construct()
            {
                
    parent::__construct();
                
    $this->login_library->is_logged();
            }

        public function 
    index()
            {
                
    $this->data = array();
                
    $query_owner_details $this->db->get_where('launch_owners', array('id' => $_SESSION['user_id']));
                if(
    $query_owner_details->num_rows()!=1)
                    {
                        
    $this->session->set_flashdata('in',1);
                    }
                
    $this->data['details'] = $query_owner_details->result_array()[0];
                
    $this->data['timezones'] = DateTimeZone::listIdentifiers(DateTimeZone::ALL);
                
    $this->load->view('header',$this->data);
                
    $this->load->view('account_view',$this->data);
                
    $this->load->view('footer_view',$this->data);
            }
        public function 
    update()
            {
                
    $this->data['timezones'] = DateTimeZone::listIdentifiers(DateTimeZone::ALL);
                if(
    in_array($_POST['timezones'],$this->data['timezones']))
                    {
                        
    $data = array(
                                
    'timezone' => $_POST['timezones']
                        );
                        
    $this->db->where('id'$_SESSION['user_id']);
                        
    $this->db->update('launch_owners'$data);
                        
    $_SESSION['timezone_updated'] = 1;
                        
    redirect(base_url('account'),'refresh');
                    }
            }    
    }
    An avergae controller
    PHP Code:
    <?php
    defined
    ('BASEPATH') OR exit('No direct script access allowed');

    class 
    Broadcast_plc_email_link extends CI_Controller {

        
        public function 
    index($plc_id)    
            {
                
    $query $this->db->get_where('launch_plcs', array('id' => $plc_id));
                if(
    $query->num_rows()==1)
                    {
                        
    $owners_hashed_email "SELECT md5(email) AS H FROM launch_plcs
                        INNER JOIN launch_owners ON launch_owners.id = launch_plcs.user_id WHERE launch_plcs.id = ?"
    ;
                        
    $query_owner $this->db->query($owners_hashed_email, array($plc_id));
                        
    $owners_hashed_email $query_owner->result_array()[0]['H'];

                        if(
    strtotime($query->result_array()[0]['broadcast_access_date']) > time())
                            {
                                if(isset(
    $query->result_array()[0]['broadcast_redirect_if_before_access_date']))
                                    {
                                        
    $this->data['full_redirect_url'] = $query->result_array()[0]['broadcast_redirect_if_before_access_date'];       
                                    }
                                else 
                                    {
                                        
    $this->data['full_redirect_url'] = base_url('errors');
                                        
    $_SESSION['error_message'] = "This page will open soon (on ".date('Y-m-d',strtotime($query->result_array()[0]['broadcast_access_date'])).")";
                                    } 
                            }
                        else 
                            {
                                
    $this->data['full_redirect_url'] = $query->result_array()[0]['url'];
                                if(
    strlen($this->data['full_redirect_url']) < 3)
                                    {       
                                        
    $this->data['full_redirect_url'] = base_url('bc-launch/'.$owners_hashed_email."/".$query->result_array()[0]['id']);
                                    }    
                            }    
                    }
                else 
                    {
                        
    $this->data['full_redirect_url'] = base_url('errors');
                        
    $_SESSION['error_message'] = "Invalid Link. Error Code 19";
                    }
                
    $this->data['full_redirect_url'];
                
    redirect($this->data['full_redirect_url'],'refresh');        
            }
    }

    A messy controller connected to library
    PHP Code:
    <?php
    defined
    ('BASEPATH') OR exit('No direct script access allowed');

    class 
    Evergreen_email_link extends CI_Controller {

        
        public function 
    index($launch_id)    
            {
                
    $this->data['launch_id']=$launch_id;
                
    $this->data['launch'] = $this->launch_library->get_launch_details($launch_id);
                
    $this->validate_admin($launch_id);
                
    $this->load->view('header',$this->data);
                
    $this->load->view('email_links_view',$this->data);            
                
    $this->load->view('footer_view',$this->data);
            }

        public function 
    redirect($launch_id$prospect_email)
            {
                
    $this->data['launch_id']=$launch_id;
                
    $this->validate($launch_id$prospect_email);
                
    $this->data['full_redirect_url'];
                
    redirect($this->data['full_redirect_url']);
            }    
        public function 
    validate_admin($launch_id)    
            {
                
    $sql "SELECT *, launch_launches.id AS LID FROM launch_launches 
                            INNER JOIN launch_owners 
                                ON launch_launches.user_id = launch_owners.id
                            WHERE launch_launches.id = ?"
    ;
                
    $query $this->db->query($sql, array($launch_id));
                if(
    $query->num_rows()!=1)
                    {
                        echo 
    "Invalid Launch Id";exit();
                    }
                else     
                    {
                        
    $this->data['launch_title'] = $query->result_array()[0]['title'];
                        return 
    $this->data['link'] = base_url("eg-redirect/".$launch_id."/[Email]");
                    }
            }
        public function 
    validate($launch_id$prospect_email)
            {
                
    $sql "SELECT *, launch_launches.id AS LID FROM launch_launches 
                            INNER JOIN launch_owners 
                                ON launch_launches.user_id = launch_owners.id
                            WHERE launch_launches.id = ?"
    ;
                
    $query $this->db->query($sql, array($launch_id));
                if(!isset(
    $prospect_email) || filter_var($prospect_emailFILTER_VALIDATE_EMAIL))
                    {
                        
    $status $this->prospects_library->can_prospect_see_sales_page($launch_id,$prospect_email);
                        
    //1 - Offer closed, 2 - Show Sales Page, 3 - Sales Page Not Opened yet
                        
    if($status==1)
                              {
                                
    $this->data['full_redirect_url'] = $query->result_array()[0]['offer_closed_page'];
                              }
                        if(
    $status==2)
                            {
                                
    $this->data['full_redirect_url'] = $query->result_array()[0]['sales_page']."?email=".$prospect_email;
                            }
                        if(
    $status==3)
                            {
                                
    $_SESSION['error_message'] = 'This page will be available soon';
                                
    $this->data['full_redirect_url'] = base_url('errors');
                            }
                        if(
    $status==5)
                            {
                                
    $_SESSION['error_message'] = 'Propsect Not In Launch. Error code 87';
                                
    $this->data['full_redirect_url'] = base_url('errors');
                            }    
                    }
                else
                    {
                        echo 
    "Invalid email";
                    }    
            }                
                        

                            
                        
                        
                    
            
        
        
    }
    A trashed out library

    PHP Code:
    <?php
    defined
    ('BASEPATH') OR exit('No direct script access allowed');

      class 
    Prospects_library
              
    {
                  
                  

                  public function 
    set_timezone($hashed_owner_email)
                    {
                      
    $CI =& get_instance();
                      
    $sql "SELECT * FROM launch_owners WHERE MD5(email) = ?";
                      
    $query_timezone $CI->db->query($sql, array($hashed_owner_email));
                      
    date_default_timezone_set(''.$query_timezone->result_array()[0]['timezone'].'');
                    }
                  public function 
    can_prospect_see_sales_page($launch_id$email)
                    {
                      
    ##  Returns 1,2 or 3
                      ##  1 - Offer closed, 2 - Show Sales Page, 3 - Sales Page Not Opened yet, 5 - Not in launch
                      ##
                      
    $return 5;
                      
    $CI =& get_instance();
                      
    $hashed_owner_email $CI->launch_library->get_hashed_owner_by_launch_id($launch_id);

                      
    $CI->prospects_library->set_timezone($hashed_owner_email);
                      
    $query $CI->db->get_where('launch_launches', array('id' => $launch_id));
                      
    $open_x_days_after_last_plc $query->result_array()[0]['open_offer_x_days_after_last_plc'];
                      
    $total_wait $query->result_array()[0]['open_offer_x_days_after_last_plc'] + $query->result_array()[0]['how_many_days_keep_sales_page_open'];

                      
    $sql "SELECT SUM(release_after_days_evergreen) AS T FROM launch_plcs WHERE launch_id = ?";
                      
    $results $CI->db->query($sql, array($launch_id));
                      
    $total_plc_waits $results->result_array()[0]['T'];
                      
    $open_sales_page_after_days $open_x_days_after_last_plc $total_plc_waits;
                      
    $all_wait $total_plc_waits $total_wait
                      
                      
    $sql "SELECT id FROM launch_prospects WHERE email = ?";
                      
    $results $CI->db->query($sql, array($email));
                      if(
    $results->num_rows()!=1)
                        {
                          return 
    5;  // 5 - Not found in launch
                        
    }
                      
    $prospects_id $results->result_array()[0]['id'];
                      
    $sql "SELECT date_added FROM launch_launch_prospect WHERE prospect_id = ?";
                      
    $results $CI->db->query($sql, array($prospects_id));
                      
    $date_added $results->result_array()[0]['date_added'];
                      
    $CI->data['offer_close_date'] = date('Y-m-d'strtotime($date_added' +'.$all_wait.' days'));  
                      
                      
    $CI->data['offer_close_date_date_format'] = date('c',strtotime($CI->data['offer_close_date']));
                      
    $days_in_launch $CI->prospects_library->how_many_days_in_launch_using_mysql($prospects_id$launch_id);
                    
                      if(
    $open_sales_page_after_days <= $days_in_launch)
                        {
                          if(
    $all_wait $days_in_launch)
                            {
                              
    $return 1;  // 1 - Offer closed
                            
    }
                           if(
    $all_wait >= $days_in_launch)
                            {
                              
    $return 2;  // Show Sales Page
                            
    }
                         } 
                      if(
    $open_sales_page_after_days $days_in_launch)         
                        {
                          
    $return 3;  // Sales Page Not Opened yet
                        
    }
                         
    // echo "<hr />";
                         // echo "<br />Open_sales_page_after_days: ".$open_sales_page_after_days;
                         // echo "<br />Keep Open: ".$all_wait."<br />"; 
                         // echo "Days in launch: ".$CI->prospects_library->how_many_days_in_launch_using_mysql($prospects_id, $launch_id);
                         // echo "<br />";
                          
    return $return;
                    }   
                public function 
    all_time_in_launch($launch_id)
                    {
                      
    $CI =& get_instance();
                      
    $sql "SELECT * FROM 
                              launch_launch_prospect_history
                              WHERE launch_id = ?"
    ;
                      
    $query $CI->db->query($sql, array($launch_id));
                      return 
    $query->num_rows();
                    }

                  public function 
    currently_in_launch($launch_id)
                    {
                      return 
    true;
    //                   $CI =& get_instance();

    // $launch_details = $CI->db->get_where('launch_launches',array('id'=>$launch_id));
    // $open_offer_x_days_after_last_plc = $launch_details->result_array()[0]['open_offer_x_days_after_last_plc'];
    // $how_many_days_keep_sales_page_open = $launch_details->result_array()[0]['how_many_days_keep_sales_page_open'];
    // $plcs = $CI->db->get_where('launch_plcs',array('launch_id'=>$launch_id));
    // $t = 0;
    // foreach($plcs->result_array() as $val=>$row)
    //   {
    //     $t += $row['release_after_days_evergreen'];
    //   }
    // $total = $t + $open_offer_x_days_after_last_plc+$how_many_days_keep_sales_page_open;

    // $since = date('Y-m-d',strtotime($total.' days ago'));

    // $to = strtotime("+".$total." day", time());
    // echo date('M d, Y', $date);

    //                   $sql = "SELECT COUNT(*) AS C FROM 
    //                           launch_launch_prospect
    //                           WHERE launch_id = ?";
    //                   $query = $CI->db->query($sql, array($launch_id));
    //                   return $query->num_rows();
                    
    }

                  public function 
    get_id_by_email($prospects_email)
                    {
                      
    $CI =& get_instance();
                      
    $sql "SELECT * FROM 
                              launch_prospects
                              WHERE email = ?"
    ;
                      
    $query $CI->db->query($sql, array($prospects_email));


                      if(
    $query->num_rows()==0)
                        {
                          echo 
    "Invalid link";exit();
                        }
                      else 
                        {
                          return 
    $query->result_array()[0]['id'];
                        }  
                    } 


                  public function 
    days_from_joining_launch($prospects_id$launch_id)
                    {
                      
    $CI =& get_instance();
                      
    $sql "SELECT date_added FROM 
                              launch_launch_prospect
                              WHERE prospect_id = ? AND launch_id = ?"
    ;
                      
    $query $CI->db->query($sql, array($prospects_id$launch_id));
                      
    $datediff strtotime('now') - strtotime($query->result_array()[0]['date_added']) ;
                      return 
    $datediff floor($datediff / (60 60 24));
                      
                    }

                  public function 
    date_added_to_launch($prospect_id$launch_id)
                    {
                      
    $CI =& get_instance();
                      
    $sql "
                      SELECT date_added 
                              FROM launch_launch_prospect 
                              WHERE launch_launch_prospect.prospect_id = ?
                              AND launch_launch_prospect.launch_id = ?
                              "
    ;
                      
    $query $CI->db->query($sql, array($prospect_id$launch_id));
                      return 
    $query->result_array()[0]['date_added'];
                    }

    public function 
    how_many_days_in_launch_using_php($prospect_id$launch_id)
      {
          
    $CI =& get_instance();
          
    $sql "SELECT date_added
                  FROM   launch_launch_prospect
                  WHERE  launch_launch_prospect.prospect_id = ?
                  AND launch_launch_prospect.launch_id = ?"
    ;
          
    $query $CI->db->query($sql, array($prospect_id$launch_id));
          return 
    floor((time() - $query->result_array()[0]['date_added']) / (60 60 24));
      }

    public function 
    how_many_days_in_launch_using_mysql($prospect_id$launch_id)
      {
        
    $CI =& get_instance();
        
    $sql "
        SELECT DATEDIFF(CURDATE(), date_added) as daysdiff
        FROM launch_launch_prospect WHERE prospect_id = ?
        AND launch_id = ?"
    ;
        
    $query $CI->db->query($sql, array($prospect_id$launch_id));
        if(
    $query->num_rows()!=1)
          {
            
    $_SESSION['error_message'] = "Invalid Request (Error Code 35. Prospect not found in launch!)";
            
    redirect(base_url('errors'),'refresh');
          }
        return 
    $query->result_array()[0]['daysdiff'];
      } 
                          

      
    public function 
    sort()
      {
        if(!isset(
    $_SESSION['sort']))
          {
            
    $_SESSION['sort'] = " launch_launch_prospect.date_added DESC";
          } 
        
    $valid_sorts = array(1,2,3);
        if(isset(
    $_GET['sort']) && in_array($_GET['sort'], $valid_sorts))
          {
            if(
    $_GET['sort']==1)
              {
                
    $_SESSION['sort'] = ' email ';
              }
            if(
    $_GET['sort']==2)
              {
                
    $_SESSION['sort'] = ' launch_launch_prospect.date_added ASC ';
              } 
            if(
    $_GET['sort']==3)
              {
                
    $_SESSION['sort'] = ' launch_launch_prospect.date_added DESC ';
              }    
          }  
          
        return 
    $_SESSION['sort'];    
      }
    public function 
    get_launch_prospects_all_time($launch_id)
        {
         
    $CI =& get_instance();
          
          
    $_SESSION['sort'] = $CI->prospects_library->sort();
          
    $sql "
            SELECT *, launch_launch_prospect.id AS LLP_ID
                    FROM launch_launch_prospect 
                    INNER JOIN launch_launches
                      ON launch_launches.id = launch_launch_prospect.launch_id 
                        AND launch_launches.user_id = ?
                        AND launch_launches.id = ?
                    INNER JOIN launch_prospects ON launch_prospects.id = launch_launch_prospect.prospect_id
                    ORDER BY "
    .$_SESSION['sort'].";
                    "
    ;
            
    $query $CI->db->query($sql, array($_SESSION['user_id'], $launch_id));
            if(
    $query->num_rows()>0)
              {
                return 
    $query->result_array(); 
              }
            else 
              {
                return 
    false;
              }  
        }  

    public function 
    get_launch_prospects_limit($launch_id,$from,$to)
        {
          
    $CI =& get_instance();

          
    $_SESSION['sort'] = $CI->prospects_library->sort();
            
    $sql "
            SELECT *, launch_launch_prospect.id AS LLP_ID
                    FROM launch_launch_prospect 
                    INNER JOIN launch_launches
                      ON launch_launches.id = launch_launch_prospect.launch_id 
                        AND launch_launches.user_id = ?
                        AND launch_launches.id = ?
                    INNER JOIN launch_prospects ON launch_prospects.id = launch_launch_prospect.prospect_id
                    ORDER BY "
    .$_SESSION['sort']."
                    LIMIT "
    .$from.", 100";
            
    $query $CI->db->query($sql, array($_SESSION['user_id'], $launch_id));
            if(
    $query->num_rows()>0)
              {
                return 
    $query->result_array(); 
              }
            else 
              {
                return 
    false;
              }  
        }



       public function 
    get_launch_prospects_evergreen($launch_id)
        {
          
    $CI =& get_instance();
          
    $_SESSION['sort'] = $CI->prospects_library->sort();
          
    $launch_details $CI->db->get_where('launch_launches',array('id'=>$launch_id));
          
    $open_offer_x_days_after_last_plc $launch_details->result_array()[0]['open_offer_x_days_after_last_plc'];
          
    $how_many_days_keep_sales_page_open $launch_details->result_array()[0]['how_many_days_keep_sales_page_open'];
          
    $plcs $CI->db->get_where('launch_plcs',array('launch_id'=>$launch_id));
          
    $t 0;
          foreach(
    $plcs->result_array() as $val=>$row)
            {
              
    $t += $row['release_after_days_evergreen'];
            }
          
    $total $t+$open_offer_x_days_after_last_plc+$how_many_days_keep_sales_page_open;

          
    $since date('Y-m-d',strtotime($total.' days ago'));
          
    $CI->data['report']= "";
          
    $CI->data['report'] .= " Total PLCs wait time: days: <strong>".$t."</strong> <br />";
          
    $CI->data['report'] .= " Sales page opens after <strong>".$open_offer_x_days_after_last_plc."</strong> says after last PLC"."<br />";
          
    $CI->data['report'] .= " Keep sales page open for <strong>".$how_many_days_keep_sales_page_open."</strong> days.<br />";
          
    $CI->data['report'] .= " Show prospects who have been in the system for <strong>".$total."</strong> days (since ".$since.")<br />";
          
    $CI->data['report'] .= $CI->prospects_library->all_time_in_launch($launch_id)." prospects have been through this launch";
          
            
    $sql "
            SELECT *, launch_launch_prospect.id AS LLP_ID
                    FROM launch_launch_prospect 
                    INNER JOIN launch_launches
                      ON launch_launches.id = launch_launch_prospect.launch_id 
                        AND launch_launches.user_id = ?
                        AND launch_launches.id = ?
                    INNER JOIN launch_prospects ON launch_prospects.id = launch_launch_prospect.prospect_id
                      AND launch_launch_prospect.date_added >= '"
    .$since."'
                    ORDER BY "
    .$_SESSION['sort']."
                    "
    ;
            
    $query $CI->db->query($sql, array($_SESSION['user_id'], $launch_id));
            
    $CI->data['report'] = "Propspets currently in the launch: <strong>".$query->num_rows()."</strong><br />".$CI->data['report'];
            if(
    $query->num_rows() > 0)
              {
                return 
    $query->result_array(); 
              }
            else 
              {
                return 
    false;
              } 

        }     

    }
    Basic view
    PHP Code:
        <!-- Page Content -->
        <div class="container">
          <div class = "row">
             <div class="col-lg-3 col-md-3 col-sm-3 portfolio-item" style = "margin-bottom: 20px;">
              <a href = "<?php echo base_url('products/add');?>"><button type="button" class="btn btn-success btn-sm">+ Add Product</button></a>
             </div> 
          </div>  
          <div class = "row">
             <table class="table table-striped table-dark">
                    <thead>
                      <tr>
                        <th>#</th>
                        <th>Product</th>
                        <th>Actions</th>
                      </tr>
                    </thead>
                    <tbody>
                         <?php
                         $count
    =0;
                          foreach(
    $plcs as $plc => $details)
                            {
                              
                              
    ?>
                                  <tr>
                                    <td><?php echo $details['id'];?></td>
                                    <td><?php echo $details['title'];?></td>
                                    <td><a href = "<?php echo base_url('edit/'.$details['id']);?>"><button type="button" class="btn btn-default btn-sm">Edit</button></a></td>
                                  </tr>
                              <?php
                            
    }
                          
    ?>
                      </tbody>
                    </table>
          </div>

        </div>
        <!-- /.container -->
    The thing is Requnix I have done several 6 figure product launches with same quality of code. I am convinced this code is not too bad. The only reason I am being a bit picky on this one is that this is way bigger and it's a B2B product. I never "sold" code before. Plus it's open and others can interact with my db. Also Kicken's code is very important for this. I feel a bit insecure about that javascript.

    I finish the functionalities in a few days, then I have to clean the controllers, libraries, sqls and the views after that.

    Then I need to do test test test. Maybe I end up using one of these as well.

    I did a launch in October I owe the code to you and Rudy to be fair. That's why I asked you if I could come and meet you. If a projects has potential to hit 7 figure $ point, it's not wise to risk the code and that's when I consider proposing partnership with someone like you or Kicken.




    Originally Posted by requinix
    Look at your Data_receiver. It's a regular controller and performs all its controller actions, but it very tightly couples the information source (real data from the database) to how the data is used (all that processing). If you can decouple them such that you can provide your own data to be processed then you can take the next step of generating fake data and handing it to the same processing code.
    Do you mean I make updates to this ONLY for the purpose of testing? Or is this a bad practise and it affects the performance?
    Last edited by English Breakfast Tea; May 4th, 2018 at 10:16 PM.
  6. #4
  7. Wiser? Not exactly.
    Devshed God 2nd Plane (6000 - 6499 posts)

    Join Date
    May 2001
    Location
    Bonita Springs, FL
    Posts
    6,272
    Rep Power
    4193
    Originally Posted by English Breakfast Tea
    Do you mean I make updates to this ONLY for the purpose of testing? Or is this a bad practice and it affects the performance?
    What requinix is suggesting is to re-structure your application in a way that makes it easier to unit-test (or similar). The general idea is you try and isolate your important business code from supporting code (like your controllers, database access, etc). That allows you to test it easier by creating fake supporting classes to feed the business code. For example you might swap out the DB access with a class that just generates random data.

    The changes would be something you make permanently, not just for testing. Depending on how large your current code base is, it may be a significant amount of work and would affect much of the code most likely. You'll have to decide if it's worth the effort or not.

    While I can understand the benefits of such architecture it's not something I have much experience with personally. Most of the stuff I work on was not designed for such things and the effort required to re-do it all isn't really worth it so it's a skill I'm a bit lacking in.


    Originally Posted by English Breakfast Tea
    As in testing, I thought I test it with one of these. Do you have any recommendations?
    Coincidentally, I'm working on migrating a system from a dedicated host to a cloud based solution and need to do some load testing to estimate what size VM's we need. I had a look at the lists you linked and ended up giving JMeter a try and it seems to work well. It took a few hours of reading and fiddling to figure out how to use it, but I was able to create a basic scenario that simulated a user on the site then ran a test simulating several concurrent users.

    You could probably do the same thing for your end. You'll need to generate some test data to feed into your script. Once you have that you could create a test scenario that submits the data to your API endpoint then crank up the number of concurrent requests and see how your script performs. You can't go crazy and try a million concurrent requests, but you could probably try a few hundred. Aside from your server, your machine running the tests has to have enough power to handle the testing. The more concurrent requests you try and run the more resources you need in both places.

    Originally Posted by English Breakfast Tea
    An avergae controller
    PHP Code:
     public function index($plc_id)    
            {
                
    $query $this->db->get_where('launch_plcs', array('id' => $plc_id));
                if(
    $query->num_rows()==1)
                    {
                        
    $owners_hashed_email "SELECT md5(email) AS H FROM launch_plcs
                        INNER JOIN launch_owners ON launch_owners.id = launch_plcs.user_id WHERE launch_plcs.id = ?"
    ;
                        
    $query_owner $this->db->query($owners_hashed_email, array($plc_id));
                        
    $owners_hashed_email $query_owner->result_array()[0]['H'];

                        if(
    strtotime($query->result_array()[0]['broadcast_access_date']) > time())
                            {
                                if(isset(
    $query->result_array()[0]['broadcast_redirect_if_before_access_date']))
                                    {
                                        
    $this->data['full_redirect_url'] = $query->result_array()[0]['broadcast_redirect_if_before_access_date'];      
                                    }
                                else
                                    {
                                        
    $this->data['full_redirect_url'] = base_url('errors');
                                        
    $_SESSION['error_message'] = "This page will open soon (on ".date('Y-m-d',strtotime($query->result_array()[0]['broadcast_access_date'])).")";
                                    }
                            }
                        else
                            {
                                
    $this->data['full_redirect_url'] = $query->result_array()[0]['url'];
                                if(
    strlen($this->data['full_redirect_url']) < 3)
                                    {      
                                        
    $this->data['full_redirect_url'] = base_url('bc-launch/'.$owners_hashed_email."/".$query->result_array()[0]['id']);
                                    }    
                            }    
                    }
                else
                    {
                        
    $this->data['full_redirect_url'] = base_url('errors');
                        
    $_SESSION['error_message'] = "Invalid Link. Error Code 19";
                    }
                
    $this->data['full_redirect_url'];
                
    redirect($this->data['full_redirect_url'],'refresh');        
            } 
    You should try and code cleaner from the get go. That doesn't mean you have to go all-out and try to split things into dedicated classes, create interfaces, etc. At the very least, just try and organize your code into functions within your controller and follow some basic guidelines, such as:

    Divide your controller code up into small functions that do specific tasks. Queries to get/save data are great candidates for this. For example, take your small bit of code to get the owner's email hash and put it into a separate method.

    You should avoid calling the same functions multiple times. Save the result to a variable and just reference that. In the function above you're referencing $query->result_array()[0] 6 times. That's an absolute waste of processor cycles, especially if result_array() does any significant work.

    While it won't affect your code's speed, your readability could also be improved by avoiding deep nesting. Use things like early return or else if to bring things back a bit.
    PHP Code:
    <?php
    defined
    ('BASEPATH') OR exit('No direct script access allowed');

    class 
    Broadcast_plc_email_link extends CI_Controller {
        public function 
    index($plc_id)    
        {
            
    $query $this->db->get_where('launch_plcs', array('id' => $plc_id));
            
    $plcDetails $query->result_array();
            if (!
    $plcDetails){
                
    //Do you need to set this variable, or could you just call:
                //  redirect(base_url('errors'), 'refresh');
                //I don't know since I don't know the full details of the code.

                
    $this->data['full_redirect_url'] = base_url('errors');
                
    $_SESSION['error_message'] = "Invalid Link. Error Code 19";
                
    redirect($this->data['full_redirect_url'], 'refresh');
                return;
            }
             
            
    $plcDetails $plcDetails[0];
            
    $owner_hashed_email $this->getOwnerHashedEmail($plc_id);
            
    $accessDate strtotime($plcDetails['broadcast_access_date']);

            if(
    $accessDate <= time())
            {
                
    $this->data['full_redirect_url'] = $plcDetails['url'];
                if(
    strlen($this->data['full_redirect_url']) < 3)
                {      
                    
    $this->data['full_redirect_url'] = base_url('bc-launch/'.$owners_hashed_email."/".$plcDetails['id']);
                }    
            }
            else if (isset(
    $plcDetails['broadcast_redirect_if_before_access_date']))
            {
                
    $this->data['full_redirect_url'] = $plcDetails['broadcast_redirect_if_before_access_date'];      
            }
            else
            {
                
    $this->data['full_redirect_url'] = base_url('errors');
                
    $_SESSION['error_message'] = "This page will open soon (on ".date('Y-m-d'$accessDate).")";
            }


            
    redirect($this->data['full_redirect_url'], 'refresh');
        }


        private function 
    getOwnerHashedEmail($plc_id){
            
    $sql "
            SELECT md5(email) AS H 
            FROM launch_plcs
            INNER JOIN launch_owners ON launch_owners.id = launch_plcs.user_id 
            WHERE launch_plcs.id = ?
            "
    ;

            
    $query_owner $this->db->query($sql, array($plc_id));
            return 
    $query_owner->result_array()[0]['H'];
        }
    }

    I read some advice a long time ago that when coding one should in general try to
    • Keep methods short enough to fit in the editor window (generally around 55 lines and 120 columns or less; depends on your editor/screen/settings)
    • Avoid nesting more than 3 levels deep within a function


    Such goals are obtainable in many situations if you break code out into well defined methods. If you keep such things in mind from the get-go you should find that your first "get it working pass" should give you some already decent code. Then your cleanup pass would be more about finding where you can consolidate duplicate code into separate methods/classes.

    Like most guidelines, don't treat them like some sort of strict rule. It's perfectly fine to violate them if need be, just take another quick look and see if you really need to or if you can re-work things in some way to meet them instead.
    Recycle your old CD's



    If I helped you out, show some love with some reputation, or tip with Bitcoins to 1N645HfYf63UbcvxajLKiSKpYHAq2Zxud
  8. #5
  9. A Change of Season
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Mar 2004
    Location
    Next Door
    Posts
    3,558
    Rep Power
    221
    Ok, Kicken,

    I am taking both your idead on board.

    Thoughts on this structure?

    Specifically, I'm curious to know how you do the redirects in Prospects_library. This situation comes up A LOT.

    From Evergreen_plc_email_link I call prospects_library->can_prospect_see_plc($plc_id, $prospect_email);

    Do you do the redirects in Evergreen_plc_email_link or prospects_library?


    PHP Code:
    <?php
    defined
    ('BASEPATH') OR exit('No direct script access allowed');

    class 
    Evergreen_plc_email_link extends CI_Controller {

        public function 
    index($plc_id$prospect_email)    
            {
                
    $this->plc_library->is_valid_plc($plc_id);
                
    $launch_id $this->plc_library->launch_id($plc_id);
                
    $launch_owner_hashed_email $this->launch_library->get_hashed_owner_by_launch_id($launch_id);
                
    $this->prospects_library->can_prospect_see_plc($plc_id$prospect_email);
            }
    }
    PHP Code:
    <?php
      defined
    ('BASEPATH') OR exit('No direct script access allowed');

      class 
    Plc_library
      
    {
       
          public function 
    redirect_url_if_before_access_date($launch_id)
              {
                
    $CI =& get_instance();
                
    $CI->launch_library->is_valid_launch($launch_id);
                
    $query $CI->db->get_where('launch_plcs', array('id'=>$launch_id));
                return 
    $query->result_array()[0]['redirect_url_if_before_access_date'];
              }
              
          public function 
    plc_url($plc_id)
            {
                
    $CI =& get_instance();
                
    $CI->plc_library->is_valid_plc($plc_id);  
                
    $sql "SELECT url FROM launch_plcs WHERE id = ?";
                
    $results $CI->db->query($sql, array($plc_id));
                return 
    $results->result_array()[0]['url'];

            }

          public function 
    redirect_to_sales_page_if_sales_page_open($plc_id)
            {
                
    $CI =& get_instance();
                
    $CI->plc_library->is_valid_plc($plc_id);  
                
    $sql "SELECT redirect_to_sales_page_if_sales_page_open AS R FROM launch_plcs WHERE id = ?";
                
    $results $CI->db->query($sql, array($plc_id));
                return 
    $results->result_array()[0]['R'];

            }

           public function 
    is_valid_plc($plc_id)
            {
              
    $CI =& get_instance();
              
    $sql="
          SELECT     launch_plcs.id 
          FROM       launch_plcs 
          INNER JOIN launch_launches 
          ON launch_launches.id = launch_plcs.launch_id
          AND        launch_launches.launch_status = ?
          where      launch_plcs.id = ? 
          "
    ;
          
          
    $query $CI->db->query($sql, array(1$plc_id));
          
          if(
    $query->num_rows()==1)
            {
              return 
    true;
            }
          else 
            {
              
    $_SESSION['error_message'] = "Invalid link. Error code 21";
              
    redirect(base_url('errors'));
            }  
        }


        public function 
    launch_id($plc_id)
        {
          
    $CI =& get_instance();
          
    $sql="
      SELECT     launch_launches.id 
      FROM       launch_plcs 
      INNER JOIN launch_launches 
      ON launch_launches.id = launch_plcs.launch_id
      where      launch_plcs.id = ? 
      "
    ;
          
          
    $query $CI->db->query($sql, array($plc_id));
          
          if(
    $query->num_rows()==1)
            {
              return 
    $query->result_array()[0]['id'];
            }
          else 
            {
              
    $_SESSION['error_message'] = "Invalid link. Error code 21";
              
    redirect(base_url('errors'));
            }  
        }  

      public function 
    releases_this_plc_after_x_days($plc_id#This includes the wait for the previous plcs as well
        
    {
          
    $CI =& get_instance();
          
    $CI->plc_library->is_valid_plc($plc_id);  
          
    $launch_id $CI->plc_library->launch_id($plc_id);
          
    $sql "SELECT SUM(release_after_days_evergreen) AS T FROM launch_plcs WHERE launch_id = ? AND id <= ?";
          
    $results $CI->db->query($sql, array($launch_id$plc_id));
          return 
    $total_plc_waits $results->result_array()[0]['T'];
        }

      }
    PHP Code:
     class Prospects_library
              
    {
    public function 
    can_prospect_see_plc($plc_id$prospect_email)
      {
          
    $CI =& get_instance();
          
    $CI->custom_functions->set_timezone_by_plc_id($plc_id);
          
    $CI->plc_library->is_valid_plc($plc_id);  
          
    $launch_id $CI->plc_library->launch_id($plc_id);
          
    $prospects_id $CI->prospects_library->prospect_id_by_email($prospect_email);
          
          
    //Release PLC After
          
    $releases_this_plc_after_x_days $CI->plc_library->releases_this_plc_after_x_days($plc_id);
          
    //Days Been In Launch
          
    $days_in_launch $CI->prospects_library->how_many_days_propspect_been_in_launch($prospects_id$launch_id);
          
    //Should Redirect To Sales Page?
          
    $total_wait_before_sales_page $CI->launch_library->total_wait_before_sales_page($launch_id);
          
    $show_evergreen_sales_page_until_x_days_since_prospect_joined_the_launch $CI->launch_library->show_evergreen_sales_page_until_x_days($launch_id);
          if(
    $days_in_launch $total_wait_before_sales_page)
            {
              if(
    $show_evergreen_sales_page_until_x_days_since_prospect_joined_the_launch >= $days_in_launch)
                {
                  
                  if(
    $CI->plc_library->redirect_to_sales_page_if_sales_page_open($plc_id))
                    {
                        
    //Redirect To Offer Page
                        
    redirect($CI->launch_library->launch_sales_page($launch_id));
                        return 
    1;
                    }
                  else 
                    {
                        
    //Show PLC
                        
    redirect($CI->plc_library->plc_url($plc_id));
                        return 
    2;
                    }  
                }
              else
                {
                  
    //Redirect To Offer Expired
                  
    $CI->launch_library->offer_closed_page($launch_id);
                  return 
    3;
                }  
            }
          if(
    $days_in_launch >= $releases_this_plc_after_x_days)
            {
                
    //Show PLC
                
    redirect($CI->plc_library->plc_url($plc_id));
                return 
    2;
            }
          if(
    $days_in_launch $releases_this_plc_after_x_days)
            {
              
    //PLC not Opened Yet
              
    redirect($CI->plc_library->redirect_url_if_before_access_date($plc_id));
              return 
    4;
            } 
        }  } 
    And guys if these are exhasting to follow don't worry about writing back I understand.

    Thanks
    Last edited by English Breakfast Tea; May 5th, 2018 at 07:54 PM.
  10. #6
  11. Lord of the Dance
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    Oct 2003
    Posts
    4,181
    Rep Power
    2011
    That code is much easier to read.
    When you come back to some code after 6+ months where you have forgot all about it, then code reading has a lot to say how fast you get in touch with it again.

    The controller is controlling the logic, so I would place any redirect calls in here.

    You should definitely not have any redirect in is_valid_plc. The prefix is_ implies it will return true or false. Keep it at that part.
    Put the function call from can_prospect_see_plc inside an if-statement and then - when false - return a status code, which tells the controller to do a redirect.

    This lead me to next point: You return a status code in the function can_prospect_see_plc, but you are not using it!?
    Last edited by MrFujin; May 6th, 2018 at 10:44 AM.
  12. #7
  13. A Change of Season
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Mar 2004
    Location
    Next Door
    Posts
    3,558
    Rep Power
    221
    What is happening now is that controllers and views are much cleaner now. I am dumping a lot into libraries.

IMN logo majestic logo threadwatch logo seochat tools logo