So I'm adding Redis to an already developed project and I'm wondering where exactly to put these cache calls.
There are existing models, and I am wondering if I can just inject redis into the models and then wrap each query with cache code, like this:
$cacheKey = "table/{$id}";
// If table entity is not in cache
if (!$predis->exists($cacheKey)) {
// Pre-existing database code
$this->db->query('SELECT * FROM table WHERE table.id = "'.$id.'" ');
$query = $this->db->get();
$result = $query->result_array();
// Set entity in redis cache
$predis->set($cacheKey, json_encode($result[0]));
return $result[0];
}
// Return cached entity from redis
return json_decode($predis->get($cacheKey), true);
But I'm just wondering if this is a dirty hack, or in fact the best way of doing things, and is it the most appropriate place to put the cache code?
I've learned from previous projects that it's better to do things the right way, the first time around!
You should start with profiling your application first and finding which parts are most often called and which are slowest.
You will get best result if you cache whole HTML parts, not single database rows. ( http://kiss-web.blogspot.com/2016/02/memcached-vs-redisphpredis-vs-predis-vs.html ).
Personally I think that Cache::remeber is best pattern:
class Cache {
protected $connection;
public function __construct($options) {
$this->connection = new Client($options);
}
public function remember($key, $closure, $expiration = 86400) {
$result = $this->connection->get($key);
if($result!==false) {
return unserialize($result);
}
$result = $closure();
$this->connection->set($key, serialize($result), 'ex', $expiration);
return $result;
}
}
Now your code would look like:
$cacheKey = "table/{$id}";
return $cache->remember($cacheKey, function() use ($id) {
$this->db->query('SELECT * FROM table WHERE table.id = "'.$id.'" ');
$query = $this->db->get();
$result = $query->result_array();
return $result[0];
});
Related
What I am trying to achieve is to create a query and use it with multiple endings:
For example
$query = User::where('is_admin', true);
if ($query->where('badge', 'red')->exists())
{
//do something
}
if ($query->where('badge', 'green')->exists())
{
//do something
}
$query->get();
$query->first();
With this, the $query variable gets overwritten every time the query is finished therefore it is not possible to re-use $query;
The current solution I have is utilizing clone
$query = User::where('is_admin', true);
$query1 = clone $query;
if ($query1->where('badge', 'red')->exists())
{
//do something
}
$query2 = clone $query
if ($query2->where('badge', 'green')->exists())
{
//do something
}
$query3 = clone $query;
$query3->get();
$query4 = clone $query;
$query4->first();
The example is used just to get the point across, not a real
life scenario.
I guess it works and does what I want, just does not feel like a laravel way of doing something, quite verbose and it gets worse with more complicated logic.
The goal is to re-use the same variable without overwriting it every time I finalize the query.
Does anybody have a better solution to this?
Is there a way to somehow break the reactivity of the $query variable?
Thanks
An elegant option is to make use of Laravel Local Query Scopes.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope a query to only include administrators.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeAdmins($query)
{
return $query->where('is_admin', true);
}
}
Now, to utilize the newly created local scope:
if (User::admins()->where('badge', 'red')->exists())
{
//do something
}
if (User::admins()->where('badge', 'green')->exists())
{
//do something
}
For starters, $query doesn't sound like a good name for fetching Users that are admins.
so, let's quickly change it so it's more clear for us to read and think
$user_admins = User::where('is_admin', true);
if ($user_admins->where('badge', 'red')->exists())
{
//do something
}
if ($user_admins->where('badge', 'green')->exists())
{
//do something
}
okay, so we grabbed admins, let's iterate through them with a foreach loop
$user_admins = User::where('is_admin', true);
foreach($user_admins as $admin) {
if ($admin->badge == 'green') {
// do something
} else if ($admin->badge == 'red') {
// do something else
}
}
now, whenever you change the $admin variable inside the loop, it will change that individual admin.
also query is the "question" eloquent is "asking" the database (for example: "SELECT * FROM users WHERE is_admin=true"; so query does not not refer to the result.
note you could also use a switch statement if you have a lot of logic, it would look like this:
$user_admins = User::where('is_admin', true);
foreach($user_admins as $admin) {
switch($admin->badge) {
case 'green:
// do something
break;
case 'red:
// do something else
break;
case 'ltblue:
case 'blue:
// do same things for ltblue and blue
break;
default: break;
}
I have a strange code from repository inspired by
"Also Ocramius (a Doctrine2 devel) say it here https://groups.google.com/d/msg/doctrine-user/RIeH8ZkKyEY/HnR7h2p0lCQJ"
public function findAll()
{
/* #var $query \Doctrine\ORM\Query */
$query = $this->getEntityManager()->createQuery();
if ($query->getQueryCacheDriver()->contains('cache_key')) {
return $query->getQueryCacheDriver()->fetch('cache_key');
}
$query = $this->getEntityManager()
->createQuery(
'SELECT c FROM AppBundle:Culture c'
);
try {
$results = $query->getResult();
$query->getQueryCacheDriver()->save('cache_key', $results, 300);
return $results;
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
I don't want to use $query->useResultCache(true, 60, 'another_cach_key'); because it parses request 'SELECT c FROM AppBundle:Culture c' which reduces performance. So I decided to use QueryCacheDriver.
But to get it I have to create empty Query!
$query = $this->getEntityManager()->createQuery();
And for invalidation in controller
$em = $this->getDoctrine()->getManager();
$em->persist($culture);
$em->flush();
// cache invalidation!
$this->getDoctrine()->getEntityManager()->createQuery()->
getQueryCacheDriver()->delete('cache_key');
I think this code smells. But don't know how to fix it! Can anyone advise best practices?
In common I want store my data and invalidate it on some events.
So I search for this title hoping someone would have already answered it however, I came across similar topics on other languages but not PHP so maybe this will help others.
I am constantly using this following script to call on the database but how can I create it so that I can make it just once at the top of the class for example and use it in every method on the class page that needs it. Example: An single page may not have all of the data it needs from the same table but if the table contains 50% of the data or more for that page, how can I modify this so that I can just say it once and let the rest of the following scripts display the data it extracted in the first place by calling it all just once?
Here's what I have now.
<?php
if($res = $dbConn->query("SELECT Column FROM Table")){
while($d = $res->fetch_assoc()){
printf("Enter HTML here with proper %s", $d['Column']);
}
}
?>
I want to call on this without the printf(" "); collect and store the data so that I can then call the results while printing or echoing the results with the HTML in other methods. What os the most efficient way? I don't want to make the same call over and over and over... well, you get the point.
Should I use fetch_array or can I still do it with fetch_assoc?
not very sure if it's the answer you want.
you can use include/include_once/require/require_once at the top of the page you want to use the function
for example:
general_function.php:
-----
function generate_form( $dbConn, $sql ) {
if($res = $dbConn->query("SELECT Column FROM Table")) {
while($d = $res->fetch_assoc()) {
printf("Enter HTML here with proper %s", $d['Column']);
}
}
}
and for those pages you want to use the function, just put
include "$PATH/general_function.php";
and call generate_form
Try this:
class QueryStorage {
public static $dbConn = null;
public static $results = [];
public static function setConnection($dbConn) {
self::$dbConn = $dbConn;
}
public static function query($query, $cache = true) {
$result = (array_key_exists($query, self::$results))?
self::$results[$query] : self::$dbConn->query($query);
if($cache) {
self::$results[$query] = $result;
}
return $result;
}
public static function delete($query) {
unset(self::$results[$query]);
}
public function clean() {
self::$results = [];
}
}
usage:
at top somewhere pass connection to class:
QueryStorage::setConnection($dbConn);
query and store it:
$result = QueryStorage::query("SELECT Column FROM Table", true);
if($result){
while($d = $result->fetch_assoc()){
printf("Enter HTML here with proper %s", $d['Column']);
}
}
reuse it everywhere:
$result = QueryStorage::query("SELECT Column FROM Table", true); // it will return same result without querying db second time
Remember: it's runtime cache and will not store result for second script run. for this purposes You can modify current class to make it
work with memcache, redis, apc and etc.
If I understood you correctly, then the trick is to make an associative array and access with its 'key' down the code.
$dataArray = array();
// Add extra column in select query for maintaining uniqness. 'id' or it can be any unique value like username.
if($res = $dbConn->query("SELECT Column,id FROM Table")){
while($d = $res->fetch_assoc()){
$dataArray[$d['id']] = $d['Column'];
}
}
//you have value in the array use like this:
echo $dataArray['requireValueId'];
//or , use 'for-loop' if you want to echo all the values
You need a function which takes in the query as a parameter and returns the result.
Like this:
public function generate_query($sql) {
if($res = $dbConn->query($sql)){
while($d = $res->fetch_assoc()){
printf("Enter HTML here with proper %s", $d['Column']);
}
}
}
I write a class to load data and export as SMARTY format through MySQLi
public function myRow($sql)
{
$this->connect();
$rec = $this->conn->query($sql);
$this->recordCount = $rec->num_rows;
if ($this->makeRecordCount) {
$this->totalRecordCount = $this->recordCount;
}
if ($this->recordCount > 0) {
$names = array();
$result = array();
$temp = array();
$count = $rec->field_count;
// Get fields name
while ($fields = mysqli_fetch_field($rec)) {
$names[] = $fields->name;
}
while ($row = $rec->fetch_assoc()) {
foreach ($names as $name) {
$temp[$name] = $row[$name];
}
array_push($result, $temp);
}
} else {
$result = null;
}
$this->conn->close();
return $result;
}
Then I can to something like
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
There are maybe many data need to be loaded in one page, and I only want to connect to database once, but I want to do it all in class, I don't want to do something like
$class->connect();
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
$sql = "SELECT * FROM `table2`";
$datas = $class->myRow($sql);
$smarty->assign('data2s', $data2s);
$class->close();
I feel it's ugly, but if I do this in class, that means I open and close connection when each data is loading, how to do it more beautiful?
maybe i'm wrong but you don't need to force a mysql connection to close because of the fact that if the connection is not persistent the php garbage collector close all connections after the script execution.
so i suggest you not to force the mysql close, let the garbage collector handle this task and only close the connection by yourself if you're sure that no more mysql transactions are required.
You simply don't need to (and shouldn't) open/close the connection inside your myRow() function.
Option 1 (naive approach): handle the connection at class level
class MyDAOClass {
private static $connection = null;
public function __construct() {
if (self::$connection === null) {
// establish connection here
}
}
public function myRow(...) {
// use self::$connection here
}
}
Option 2:
Handle the connection from outside the class altogether (possibly in a singleton class), since all objects from your application probably can share the same object.
Your second suggestion is what I would do.
$class->connect();
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
$sql = "SELECT * FROM `table2`";
$datas = $class->myRow($sql);
$smarty->assign('data2s', $data2s);
$class->close();
You connect to the database once. As PHP is single threaded, you will load the first result, then go right away and load the second result. Once everything is done, you close the connection. No connection is kept alive longer than it has to, which is good.
What I usually do it make a method associated with Smarty that closes my database-connection too. That way I don't have to worry about closing it.
Something like this:
<?php
// Store reference to Smarty in Class
$class->setSmarty($smarty);
[...]
// Done with all database fetching, now display the template
$class->display('my_template.tpl');
[...]
// Example inplementation of the class
Class YourClass {
private $smarty;
public function setSmarty($smarty) {
$this->smarty = &$smarty;
}
public function display($tpl) {
$this->close();
$this->smarty->display($tpl);
}
}
?>
The best example i can think of is a hall of fame page for sports.
You can then have a navigation that can limit the results depending on the user's requests, Past 3 Months and boxing for example.
What would be the best way to display multiple kind of result layouts, for example,
The swimming results have a different layout to football, football differs to boxing and then add a time limiter by timestamp.
These are the options i have thought of so far and would like your opinion on.
Simple PHP
if($_GET['sport'] = "boxing"){
if(isset($_GET['timescale'])){
$start = 1334752108;
$end = 1334759108;
$query = "SELECT athlete_name, athlete_age, athlete_desc FROM `athletes` WHERE `timestamp` BETWEEN '".$start."' AND '".$end."' AND `sport` = 'boxing' LIMIT 30";
} else {
$query = "SELECT athlete_name, athlete_age, athlete_desc FROM `athletes` WHERE `sport` = 'boxing' LIMIT 30";
}
while($row = mysql_fetch_array(mysql_query($query))){
echo "<div class='boxing'>";
//show results
echo "</div>";
}
}
if($_GET['sport'] = "swimming"){
//repeated
}
if($_GET['sport'] = "football"){
//repeated
}
Ajax
Have either one page that will handle all the requests (ajax_request_hof.php) that contains similar code to the PHP above.
EDIT
skafandri suggested a Data Mapping Class, would anyone else advise this or is able to show me an example?
Any ideas on how i can improve this are greatly welcomed and needed.
You mentioned in SO chat that you didn't have any experience with frameworks so here's a suggestion without one.
I know I'm probably going to be flamed for this, but knowing that it's an organizational structure issue you can steal some concepts of MVC and use them until you can port it to a more proper structure to at least make it a little cleaner and a lot easier to manage.
Disclaimer: In no way is this a good structure but for your problem, considering you told me you didn't have any background on OOP and or patterns, but it's "good enough" as a temporary solution.
If you order it like this you can insert it into almost any framework without doing a lot of work.
Here's one way you can do it.
With the following classes:
<?php
class BasketballPage
{
protected $mapper;
public construct ( $mapper )
{
$this->mapper = $mapper;
}
public function display_player_info( $playerid, $sportid )
{
$basketball_player = $this->mapper->get_sports_player( $playerid, $sportid )
echo '<p>Basketball player name ' . $basketball_player['name'];
echo '<p>Some other HTML, etc</p>';
}
public function display_match_data($matchid, $sportid)
{
//Same as above but would call to $this_mapper->get_match_data(); And display relevant HTML.
}
public function display_player_info_AJAX( $playerid, $sportid )
{
$player = $this->mapper->get_sports_player();
header('Content-type: text/json');
header('Content-type: application/json');
echo json_encode( $player );
exit();
}
}
class BoxingPage
{
protected $mapper;
public function display_player_info( $playerid, $sportid)
{
$boxing_person = $this->mapper->get_sports_player( $playerid, $sportid)
echo '<p>Person\'s boxing name ' . $boxing_person['name'];
echo '<p>Some other HTML, etc</p>';
}
}
class Mapper
{
protected $connection;
public function __construct ( $connection )
{
$this->connection = $connection;
}
public function get_sports_player($id, $sportid)
{
$query = $this->connection->prepare( 'SELECT * FROM players WHERE id = :player_id AND sport_id' );
$query->bindValue(':player_id', $id, PDO::PARAM_INT);
$query->bindValue(':sport_id', $sport_id, PDO::PARAM_INT);
$query->execute();
return $query->fetchAll( PDO::FETCH_ASSOC );
}
public function get_match_data($matchid, $sportid)
{
//some query here that returns match data.
}
}
?>
You'd have a single php page for each of these classes, since they can grow big.
I.e:
Basketballpage.php
Boxingpage.php
Mapper.php
Then include these files into your index.php. and your index could look something like this:
index.php?playerid=1&sportid=2
<?php
//Instantiate the classes first.
$connection = new PDO($dsn,$username,$password); //your database credentials
$mapper = new Mapper( $connection );
$basketballpage = new BasketballPage( $mapper );
$boxingpage = new BoxingPage( $mapper );
if( $_GET['page'] == 2]) //lets say basketball is id 2 in your database
{
$basketballpage->display_player_info( $_GET['playerid'], $_GET['sportid'] );
}
//In this same page you'd also add this other line, but lets say they visit the bottom link instead: index.php?playerid=1&sportid=3
if( $_GET['page'] == 3]) //lets say boxing is 3 in your database
{
$boxing->display_player_info( $_GET['playerid'], $_GET['sportid'] );
}
//THEN lets say someone visits a different link like: index.php?index.php?matchid=1&sportid=2&page=2
if( $_GET['page'] == 2] && $_GET['matchid'])
{
$boxingpage->display_match_data( $_GET['playerid'] );
}
//On the above you can use that same category, but a different function will display a different page!
//Bonus example, you can use it for AJAX easily. Lets say they visit the url index.php?playerid=1&sportid=2&AJAX=1
if( $_GET['page'] == 2 && GET['AJAX'] == 1)
{
$basketballpage->display_player_info_AJAX( $_GET['playerid'], $_GET['sportid'] );
}
?>
It seems complicated but once you see how it's all connected, notice how your index page is only around 30-40 lines! It can make things really neat and you can just concentrate on routing your requests on index.php while the other files take care of the rest.
EAV is a great solution to handle different data models, you can also write a data mapping class, although I recommend you to use a PHP framework, why not ZEND?
The basic ideas to solving this well are:
Split your data retrieval from your display.
class Model { public function getData() {} }
class View { public function write() {} }
$model = new Model();
$view = new View();
$view->write($model->getData());
Implement Model::getData:
Loop to build the sports that you are interested in so that you can OR them in a query.
Get all of the results.
Use PHP to order the array into sports.
Implement View::write:
Loop over each sport, displaying it correctly.