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;
}
Related
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];
});
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 am currently writing an adress book and using a framework (CakePHP) an MVC for the first time. Unfortunately I have some trouble.
I want to realize the following:
In case the URL is
/contacts/view/
I want to show all contacts in a list. In case there is an id given after /view/, e.g.
/contacts/view/1
I just want to display the contact with the id 1. (complete different view/design than in the first case)
My ContactsController.php is the following
public function view($id = null){
if(!$this->id){
/*
* Show all users
*/
$this->set('mode', 'all');
$this->set('contacts', $this->Contact->find('all'));
} else {
/*
* Show a specific user
*/
$this->set('mode','single');
if(!$this->Contact->findByid($id)){
throw new NotFoundException(__('User not found'));
} else {
$this->set('contact', $this->Contact->findByid($id));
};
}
}
But "$this->mode" is always set as "all". How can I check whether the id is set or not?
I really want to avoid "ugly" URL-schemes like ?id=1
Thanks in advance!
Your code is only meeting the if part and its not going to else part. Use (!$id)..
$_GET data is retrieved through the URL. In CakePHP this means it's accessed through that method's parameters.
I'm arbitrarily picking names, so please follow! If you're in the guests controller and posting to the register method you'd access it like this
function register($param1, $param2, $param3){
}
Each of these params is the GET data, so the URL would look something like
www.example.com/guests/param1/param2/param3
So now for your question How can I check whether the id is set or not?
There are a couple of possibilities. If you want to check if the ID exists, you can do something like
$this->Model->set = $param1
if (!$this->Model->exists()) {
throw new NotFoundException(__('Invalid user'));
}
else{
//conduct search
}
Or you can just search based on whether or not the parameter is set
if(isset($param1)){ //param1 is set
$search = $this->Model->find('all','conditions=>array('id' => $param1)));
}
else{
$search = $this->Model->find('all');
}
You should only change the conditions not the whole block of code like
public function view($id = null){
$conditions = array();
$mode = 'all';
if($id){
$conditions['Contact.id'] = $id;
$mode = 'single';
}
$contacts = $this->Contact->find('all', array('conditions' => $conditions));
$this->set(compact('contacts', 'mode'));
}
I would like to do these actions step by step:
first DB update
copy file
unlink file
second DB update
It is working, but I don't know if my code is correct/valid:
$update1 = $DB->query("UPDATE...");
if ($update1)
{
if (copy("..."))
{
if (unlink("..."))
{
$update2 = $DB->query("UPDATE ...");
}
}
}
Is it possible to use if statement this way?
I found that it is usually used with PHP operators and PHP MySQL select, for example:
$select = $DB->row("SELECT number...");
if ($select->number == 2) {
...
}
Sure, your ifs work fine. What would look and flow better would be using a function like this:
function processThings() {
// make sure anything you use in here is either passed in or global
if(!$update1)
return false;
if(!$copy)
return false;
if(!$unlink)
return false;
if(!$update2)
return false;
// you made it!
return true;
}
make sure you call $DB as a global variable, plus pass in whatever strings you need etc etc
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.