I have been working with a small team on a small project about fiction world building. I have been assigned with the task of managing triggers/chained behavior of entities (rocks/places/items) that can be triggered in many ways such as throwing a magic rock into the lake and monster X will appear, and continue to trigger the things in a chain until it reaches the end.
I have tried this
$Trigger_123 = new stdClass();
$Trigger_123->name = "Name";
$Trigger_123->renderOn = ? // (object_345->throwedInLakeX) ?
How can I store this in MySQL? Can I have it checked if its a chain part? Also I tried MySQL triggers but I can't find a way to execute PHP on those triggers. Running PHP code on update or delete for example.
Cron jobs was not a option because many things will be added in the future and cron jobs will take a lot of time to finish, I was hoping of finding a more php-based solution.
Edited (adding some additional information)
I tried to implement this in many ways. I ended up with a system of dependecies pretty much like debian packages which I believe is not suited for this.
Database structure
Table "object"
--------------
ID (int)
Name (varchar)
Table "triggers"
----------------
ID (int)
Name (varchar)
Data (blob) // usually, I store php code and use eval to run
Table "attributes"
------------------
ID (int)
attribute (varchar)
value (blob)
Table "object_has_triggers"
---------------------------
ID (int)
ObjectID (int)
TriggerID (int)
Table "object_has_attributes"
-----------------------------
ID (int)
ObjectID (int)
AttributeID (int)
What I want as a result is to make a PHP code snippet execute each time
A database transaction , before is submitted and after to database
A object that has X triggers attached to it, resolve them
Each Trigger that is triggered by X be checked if all dependecies to it are satisfied
Question:
Is something like this even possible to build with PHP Or should I try other scripting languages like python?
what i want as a result is to make a PHP code snippet execute each time
A database transaction , before is submitted and after to database
You should call PHP function in trigger. Write all logic in PHP which required to invoke.
A object that has X triggers attached to it, resolve them
A object that has X triggers attached to it, rather convert it into PHP code or resolve it in PHP.
Each Trigger that is triggered by X be checked if all dependecies to it are satisfied
You can make one database table for saving responses after successful completing trigger. And at last you can check all dependencies are satisfied or not.
For calling PHP function from trigger, see different answers of following posts for different types of solutions.
Invoking a PHP script from a MySQL trigger
A PHP code snippet execute each time a database transaction , before is submitted and after to database
Don't reinvent the wheel, this has an incredible simple solution: have a layer on top of your database calls.
Instead of querying your database directly, call a function (perhaps in an object) that handles the database insertion of triggers. And it is right there that you can add your code to pre and post- process your triggers in whichever way you please.
function processDatabaseInsertion($trigger) {
//Preceding code goes here
//Database transaction goes here
//Post-processing code goes here
}
$Trigger_123 = new stdClass();
$Trigger_123->name = "Name";
$Trigger_123->renderOn = $object_345->throwedInLakeX;
processDatabaseInsertion($Trigger_123);
Over simplified, but you get the idea. I would recommend writing a custom class for your triggers but I wrote it in a procedural style since I don't know if you are familiar with OOP.
A PHP code snippet execute each time a object that has X triggers attached to it, resolve them
Same principle as before. If you use PHP >= 5.3, you can spice it up a bit using closures:
function processDatabaseInsertion($trigger) {
//Preceding code goes here
$trigger->renderOn();
//Database transaction goes here
//Post-processing code goes here
}
$Trigger_123 = new stdClass();
$Trigger_123->name = "Name";
$Trigger_123->renderOn = function() use ($Trigger_123) { doAwesomeThing($Trigger_123); }
processDatabaseInsertion($Trigger_123);
Or otherwise go for a more traditional approach:
function processDatabaseInsertion($trigger) {
//Preceding code goes here
switch($trigger->renderOn) {
case "awesomeThing":
doAwesomeThing($trigger);
break;
case "anotherThing":
break;
default:
break;
}
//Database transaction goes here
//Post-processing code goes here
}
$Trigger_123 = new stdClass();
$Trigger_123->name = "Name";
$Trigger_123->renderOn = "awesomeThing";
processDatabaseInsertion($Trigger_123);
Each Trigger that is triggered by X be checked if all dependecies to
it are satisfied
Can easily be handled by the methods above with some more PHP logic as you will be able to tell. You may want to have for instance a generic function called every time you need to process a trigger which in turns check if dependencies are satistifed and runs the specific trigger if so.
Either way there are better ways to tackle this problem than to use eval or some mysql trigger hacking as you can see :)
Related
I'm new to this and I know I'm probably doing this entire thing the wrong way, but I've been at it all day trying to figure it out. I'm realizing there's a big difference between programming a real project of my own rather than just practicing small syntax-code online. So, I lack the experience on how to merge/pass different variables/scopes together. Understanding how to fit everything within the bigger picture is a completely different story for me. Thanks in advance.
What I'm trying to do, is to make the function "selectyacht" output data in a different location from where it's being called (in viewship.php). The output data (in viewship.php) needs to be only certain fields (not everything) returned and those results will be scattered all over the html page (not in a table). In addition to that, I have this variable: "$sqlstatement" (in sqlconn.php) that I'm trying to bring outside the function because I don't want to repeat the connection function every time. I tried a global variable, as much as I shouldn't, and it thankfully it gave me an error, which means I have to find a better way.
Basically my struggle is in understanding how I should structure this entire thing based on two factors:
To allow the second conditional statement in sqlconn.php to be typed
as least often as possible for different "selectyacht" functions
that will come in the future.
To allow the connection instance in sqlconn.php to reside outside the function since it will be used many times for different functions.
Returning data in a different place from where it's being called in viewship.php because the call will be a button press, not the results to be shown.
This is probably very simple, but yet it eludes me.
P.S. Some of this code is a copy/paste from other resources on the internet that I'm trying to merge with my own needs.
sqlconn.php
<?php
$servername = "XXXXXXXX";
$username = "XXXXXXXX";
$password = "XXXXXXXX";
$dbname = "XXXXXXXX";
// Instantiate the connection object
$dbconn = new mysqli($servername, $username, $password, $dbname);
// Check if the connection works or show an error
if ($dbconn->connect_error) {
die("Connection failed: " . $dbconn->connect_error);
}
// Create a query based on the ship's name
function selectyacht($shipname) {
global $sqlstatement;
$sqlstatement = "SELECT * FROM ships WHERE Name=" . "'" . $shipname . "'";
}
// Put the sql statement inside the connection.
// Additional sql statements will be added in the future somehow from other functions
$query = $dbconn->query($sqlstatement);
// Return the data from the ship to be repeated as less as possible for each future function
if ($query->field_count > 0) {
while($data = $query->fetch_assoc()) {
return $data;
}
}
else {
echo "No data found";
}
// Close the connection
$dbconn->close();
?>
viewship.php
<html>
<body>
<?php include 'sqlconn.php';?>
<!-- ship being selected from different buttons -->
<?php selectyacht("Pelorus");?>
<br>
<!-- This is the output result -->
<?php echo $data["Designer"];?>
<?php echo $data["Length"];?>
<?php echo $data["Beam"];?>
<?php echo $data["Height"];?>
</body>
</html>
Mate, I am not sure if I can cover whole PHP coding standards in one answer but I will try to at least direct you.
First of all you need to learn about classes and object oriented programming. The subject itself could be a book but what you should research is autoloading which basically allows you to put your functions code in different files and let server to include these files when you call function used in one of these files. This way you will be able to split code responsible for database connection and for performing data operations (fetching/updating/deleting).
Second, drop mysqli and move to PDO (or even better to DBAL when you discover what Composer is). I know that Internet is full of examples based on mysqli but this method is just on it's way out and it is not coming back.
Next, use prepared statements - it's a security thing (read about SQL injection). Never, ever put external variables into query like this:
"SELECT * FROM ships WHERE Name=" . "'" . $shipname . "'";
Anyone with mean intentions is able to put there string which will modify your query to do whatever he wants eg. erase your database completely. Using prepared statements in PDO your query would look like this:
$stmt = $this->pdo->prepare("SELECT * FROM ships WHERE Name = :ship_name");
$stmt->bindValue(':ship_name', $shipname);
Now to your structure - you should have DB class responsible only for database connection and Ships class where you would have your functions responsible eg. for fetching data. Than you would pass (inject) database connection as an argument to class containing you selectYacht function.
Look here for details how implementation looks like: Singleton alternative for PHP PDO
For
'Returning data in a different place from where it's being called'
If I understand you correctly you would like to have some field to input ship name and button to show its details after clicking into it. You have 2 options here:
standard form - you just create standard html form and submit it with button click redirecting it to itself (or other page). In file where you would like to show results you just use function selectYacht getting ship name from POST and passing it to function selectYacht and then just printing it's results (field by field in places you need them)
AJAX form - if you prefer doing it without reloading original page - sending field value representing ship name via AJAX to other page where you use selectYacht function and update page with Java Script
I have a query in a CakePHP 3.0 table with a formatResults() method applied to it.
In order to carry out this calculation, data is required from a table in a different database (Currencies).
I am currently using this code to gather the data and pass it to the function:
$currencies = TableRegistry::get('Currencies');
$currencyValues = $currencies
->findByCurrno($options['currency'])
->cache('currency'.$options['currency']);
$currencyValues = $currencyValues->first()->toArray()
$query->formatResults(function ($results) use ($currencyValues) {
return $results->map(function($row) use ($currencyValues) {
$row['converted_earnings'] = $row['earned'] / $currencyValues['cur'.$row['currency']];
$row['converted_advances'] = $row['advances'] / $currencyValues['cur'.$row['currency']];
return $row;
});
});
The problem is, this code seems to take a very long time to execute, even though it is only iterating through a few hundred rows of data.
Further investigation revealed that if I do not collect the data from the 'currencies' table and instead declare $currencyValues as an array with fixed numbers the code takes a full second less to execute.
Through commenting out parts of the code, I have isolated this to be the cause of the problem:
$currencyValues = $currencies
->findByCurrno($options['currency'])
->cache('currency'.$options['currency']);
If I remove this part of the code then everything runs quickly, and as soon as I add it in (even if I do not use the data it returns and use the static array) the page takes far longer to load. It should be noted that this problem occurs whether or not I use the ->cache() method, and that the query itself reports that it takes 0-1ms in the sql dump.
My question is - why is this slowing down my execution so much and how can I stop it? It is pretty much a requirement that I use the data from this table for the operation, so I am looking for a way to speed it up.
A bit of weird issue - I am looking to enter data into two separate
collections (same db) and I am getting totally weird results. I am
sure it is the way I am doing this but it is the results which have me
a bit baffled.
Here is a snippet of what I am trying to accomplish:
$mynewconnection = new Mongo(); // create new mongo connection
$collectionDB = $mynewconnection->Datadb; // select db
$collectionA = $collectionDB->DataA; // select collectionA
$collectionB = $collectionDB->DataB; // select collectionB
/* Go off and chop up data and create CriteriaA/B */
$insertA = $collectionA->insert($criteriaA);
$insertB = $collectionB->insert($criteriaB);
So what I am trying accomplish is to enter part of the data set into
one collection and the other part of the dataset into another
collection. What is happening is that sometimes the data will be
entered into Both collections (as desired) and other times data will
be entered into just collectionA and yet other times data will be
entered into just collectionB.
Anyone have any ideas on what I am missing or what would be causing
this strange behavior?
When you fire a query to write something to MongoDB, it does not confirm whether the data is written to database or not. You need to see the page of php's manual for Write Concerns.
The link for the same is: http://www.php.net/manual/en/mongo.writeconcerns.php
I am trying to create a plugin that will take the value of a listbox TV and set the document's createdby field to match that TV's setting onDocFormSave. The TV populates itself automatically with all active users and output's their ID.
I have the following code for the plugin, but when I try to save any resource it simply hangs and never saves. setCreatedBy is the name of the listbox TV:
switch ($modx->event->name) {
case 'onDocFormSave':
$created_by = $resource->getTVValue('setCreatedBy')
if ($resource->get('createdby') != $created_by) {
$modx->resource->set('createdby', $created_by));
}
break;
}
Untested.
It looks like setting also has to be done on the resource, not via the Modx-class.
$resource->set('createdby', $created_by); // You also have a ) too much in your code.
Inspected the docs.
If you omit the $resource->set... and run the plugin, will it pass? I'm wondering if you might be causing a loop, i.e $resource->set triggers another onDocFormSave. Do you have access to the server error.log? It probably contains whatever is crashing.
Those on the Modx forums were able to give me a leg up.
switch ($modx->event->name) {
case 'OnDocFormSave':
$created_by = $resource->getTVValue('setCreatedBy');
if (!empty($created_by) && $resource->get('createdby') != $created_by) {
$resource->set('createdby', $created_by);
$resource->save();
}
break;}
For reference, the way I handled gathering the names and user id's of Modx users and placing them in a selectbox TV was to use the Peoples snippet in an #EVAL binding:
#EVAL return $modx->runSnippet('Peoples',array('tpl'=>'peoplesTpl','outputSeparator'=>'||','active'=>'1'));
This is a petty dirty and slow way of doing things, but a request to have this be a standard field on Modx resources has been submitted to GitHub
I'm looking into doing some long polling with jQuery and PHP for a message system. I'm curious to know the best/most efficient way to achieve this. I'm basing is off this Simple Long Polling Example.
If a user is sitting on the inbox page, I want to pull in any new messages. One idea that I've seen is adding a last_checked column to the message table. The PHP script would look something like this:
query to check for all null `last_checked` messages
if there are any...
while(...) {
add data to array
update `last_checked` column to current time
}
send data back
I like this idea but I'm wondering what others think of it. Is this an ideal way to approach this? Any information will be helpful!
To add, there are no set number of uses that could be on the site so I'm looking for an efficient way to do it.
Yes the way that you describe it is how the Long Polling Method is working generally.
Your sample code is a little vague, so i would like to add that you should do a sleep() for a small amount of time inside the while loop and each time compare the last_checked time (which is stored on server side) and the current time (which is what is sent from the client's side).
Something like this:
$current = isset($_GET['timestamp']) ? $_GET['timestamp'] : 0;
$last_checked = getLastCheckedTime(); //returns the last time db accessed
while( $last_checked <= $current) {
usleep(100000);
$last_checked = getLastCheckedTime();
}
$response = array();
$response['latestData'] = getLatestData() //fetches all the data you want based on time
$response['timestamp'] = $last_checked;
echo json_encode($response);
And at your client's side JS you would have this:
function longPolling(){
$.ajax({
type : 'Get',
url : 'data.php?timestamp=' + timestamp,
async : true,
cache : false,
success : function(data) {
var jsonData = eval('(' + data + ')');
//do something with the data, eg display them
timestamp = jsonData['timestamp'];
setTimeout('longPolling()', 1000);
},
error : function(XMLHttpRequest, textstatus, error) {
alert(error);
setTimeout('longPolling()', 15000);
}
});
}
Instead of adding new column as last_checked you can add as last_checked_time. So that you can get the data from last_checked_time to the current_time.
(i.e) DATA BETWEEN `last_checked_time` AND `current_time`
If you only have one user, that's fine. If you don't, you'll run into complications. You'll also run one hell of a lot of SELECT queries by doing this.
I've been firmly convinced for a while that PHP and long polling just do not work natively due to PHP not having any cross-client event-driven possibilities. This means you'll need to check your database every second/2s/5s instead of relying on events.
If you still want to do this, however, I would make your messaging system write a file [nameofuser].txt in a directory whenever the user has a message, and check for message existence using this trigger. If the file exists and is not empty, fire off the request to get the message, process, feed back and then delete the text file. This will reduce your SQL overhead, while (if you're not careful) increasing your disk IO.
Structure-wise, an associative table is by far the best. Make a new table dedicated to checking the status, with three columns: user_id message_id read_at. The usage should be obvious. Any combination not in there is unread.
Instead of creating a column named last_checked, you could create a column called: checked.
If you save all messages in the database, you could update the field in the database. Example:
User 1 sends User 2 a message.
PHP receives the message using the long-polling system and saves the message in a table.
User 2, when online, would send a signal to the server, notifying the server that User 1 is ready to receive messages
The server checks the table for all messages that are not 'checked' and returns them.