Help Request | PHP + MySQL + Botman.io | Variable Scope


#1

Hi all!

So I Found this awesome PHP framework for building chatbots called Botman.io .
I am working on building an RW plugin that will allow for an easy way to build out a chatbot that can be managed through a web-based GUI.

At the moment I am stuck finding a way to automate the script to create triggers and responses from a SQL table. My script is able to create a working trigger for each row in the table. However, I can’t seem to pull in the response from the corresponding row. I know this has to do with variable scope and have made several attempts of passing the variable into the function with no success.

If anyone has any input on what I’m doing wrong, what would work, or what I should google, I would really appreciate it.

Cheers!

Brandon

$servername = "localhost";
$username = "USR";
$password = "PASS!";
$dbname = "DB";
 	


$botman = resolve('botman');



// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
} 

$sql = "SELECT id, command, responce, function FROM commands_general";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
 $botman->hears($row['command'], function ($bot) {
    	$bot->reply($row['responce']);  	
		});
	

} 

    
 

} else {
echo "0 results";
}

$conn->close();

#2

Should responce not be response?


#3

Only if I was coding in English 😆

Good catch. Doesn’t hurt the function but could screw up another developer if I ever bring someone onto the project.


#4

So I’m currently trying something that looks like this.
It creates a global variable and I can pass a response from the table to reply. However only by manually defining to use $responceX (ie. $responce2). I thought a solution might be to use a counter to add the corresponding ID to the end of the variable.

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
} 

$sql = "SELECT id, command, responce, function FROM commands_general";
$result = $conn->query($sql);


if ($result->num_rows > 0) {

$counter = 0;

while($row = $result->fetch_assoc()) {

$counter++;

$GLOBALS['id'] = $row["id"];
$responceId = 'responce'.$row["id"];
$GLOBALS[$responceId] = $row['responce'];


static $counter;

 $botman->hears($row['command'], function ($bot) {
       static $counter;

        global $id;
 			global $responce2;
    	$bot->reply($counter);  
    	
		});
		
		
}    
} else {
echo "0 results";
}

$conn->close();

EDIT:

This is what the code looked like when it was passing the variable.

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
} 

$sql = "SELECT id, command, responce, function FROM commands_general";
$result = $conn->query($sql);

if ($result->num_rows > 0) {

    while($row = $result->fetch_assoc()) {

//$command = $row['command'];
$responceId = 'responce'.$row["id"];
$GLOBALS[$responceId] = $row['responce'];


 $botman->hears($row['command'], function ($bot) {
 			global $responce2;
        	$bot->reply($responce2);  	
		});
    }    
} else {
    echo "0 results";
}

$conn->close();

#5

It’s not really obvious to me what you’re attempting to do here. And the chaos of bad indentation created by posting here doesn’t help.

To help people help you I might recommend adding some more comments to the code to say what the main conditionals are doing and perhaps what these global variables are storing.

Breaking things down into a few functions that have obvious input parameters and return values might help too.


#6

@isaiah, that makes sense.

I’ll start with some more context.
Here is my SQL table:


It has an ID, Command, Responce, and Function for each row .

Here is what the function looks like to define a command and response for a BotMan chatbot:

   $botman->hears('Can you hear me', function ($bot) {
        $bot->reply('You are coming in loud and clear');
    });

Rather than code one of these functions for each command/response, I would like to automatically generate the function for each row in the table.

I start by connecting to the SQL database and querying the commands_general table.

<?php
//Define Variables for connecting to MySQL Database
    $servername = "localhost";
    $username = "USR";
    $password = "PASS";
    $dbname = "DB_NAME";
     	
// Create a connection to MySQL database with commands and responses
    $conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    } 

//Define which data to retrieve and from which table in the database
    $sql = "SELECT id, command, responce FROM commands_general";

//Get the results from the query in $sql     
    $result = $conn->query($sql);

Now it’s time to create a command and response from each row in the commands_general table.

//If there is at least one row in the results then continue
    if ($result->num_rows > 0) {

    //Set a global variable $counter that will be used to pass the correct response into the  $botman->hears function
        $GLOBALS['counter'] = 0;

    //Loop through each Row in the results
        while($row = $result->fetch_assoc()) {

            //Create a variable for each row based on the ID for that row in the table
                $responceId = 'responce'.$row["id"];

           //This will create $responce1, $responce2, $responce3 and assign the 'responce' associated with the row/ID ($responce1 = 'Hello Brandon', $responce2 = 'it works', $responce3 = 'you rock')
                $GLOBALS[$responceId] = $row['responce'];   

          //Here is where thisngs get ticky due to variable scopes
               $botman->hears($row['command'], function ($bot) {

                   //Pull in the $counter variable from outside of this function
                       static $counter;

                  //Create a variable name using the $counter variable which should result in a variable that corrisponds with a command/responce from the table
                       $response = 'reponce'.$counter;
                        global $$response;

                 //Set the reply to be the value of the variable name generated by $responce (i.e. responce1, responce2, responce3)
        	           $bot->reply($$response);  	
		    });
		// Add 1 to the $counter variable each loop.   	 
	       $GLOBALS['counter']++;
    }    
} 
//Close the SQL Connection
    $conn->close();

The issue that I am having now is that when I put $GLOBALS[‘counter’] inside the $botman->hears function It always equals ‘3’. This means that every command responds with $responce3 = ‘you rock’.

So rather than adding 1 to the counter, then creating the $botman->hears function, then adding 1 to the counter and repeating, I am adding 1 to the counter for each row and then passing the final count to the $botman->hears function.


#7

Here are my first suggestions:

  • spell things correctly. you have too many misspellings: response, reponceId, ‘reponce’ – this does not bode well for understanding what the heck is going on. This may seem like a little detail, but that’s the kind of silly error that can eat an afternoon of frustration. as a horrendous dyslexic that misspells EVERYTHGN i can attest to how often this one has bit me.

  • along with the spelling problems is some interesting use of string references to things – with no error checking 😬.i would just define all your strings right at the top of your file as constants. that way you never have to worry about accidentally typing those strings incorrectly.

  • “separation of concerns” isn’t just one of the SOLID principles of programming, it’s a bid deal. you have an SQL connection, an SQL quarry, plus the use of the bowman library all in a single function. there is WAY too much going on to understand anything well enough to debug. the SQL connection details should be its very own function, so should the query. once the query returns you should call a function and pass the necessary query details to a function that does whatever it needs to with the botman library. keeping each little things separate will will make your variable names much less cumbersome – and make debugging a lot simpler. you can check that when you call the function that the values you think are passed in actually are.

  • stop using globals. it’s not obvious why you’re doing this. i doubt it’s necessary. it rarely is. it’s one of those things that seems to make things easier until you have to debug things, then it makes everything harder. and you always have to debug things, so best just to never use those. 😉

  • i’m not sure why you’re doing “static $counter” – this seems odd. i’m not even sure what the “static” keyword does when used inside of a function closure.
    in general the static keyword makes an instance variable into a class variable. or a loop variable into an out-side-loop variable. (it also has other implementation details for old school C – but whatever) – but i’m not sure what the intention is here. it seems unlikely to do what your comment says it does. if it does do that thing, my mind is definitely blown, and i’d say that’s a very odd use of that and i might choose some other way to do it that’s a bit more obvious.

once that’s done. i’d just try printing out some intermediary values to see what your counters are.


#8

Thanks, @isaiah!

Fair point on the spelling. Fixed that up.

I’ve simplified things a bit. Here is my SQL connection and function.

<?php

$generalCommands = generalCommandsQuery();


// Return $generalCommands in the form of an object array
function generalCommandsQuery() {

	$servername = "localhost";
	$username = "USR";
	$password = "PASS";
	$dbname = "DB_NAME";
     	
	// Create connection
	$conn = new mysqli($servername, $username, $password, $dbname);
	
	// Check connection
	if ($conn->connect_error) {
    	die("Connection failed: " . $conn->connect_error);
	} 

	$generalCommandsQuery = "SELECT id, command, response, function FROM commands_general";
	$generalCommands = $conn->query($generalCommandsQuery);

	return $generalCommands;
	
	$conn->close();
}

Now all I need to do is figure out how to get $row['response'] to work in :

while($row = $generalCommands->fetch_assoc()) {
	$botman->hears($row['command'], function ($bot) {
    	$bot->reply($row['response']);  	
    });		
}