first question here, because i'm stuck since this morning and i don't find a single way to fix my problem.
I'm trying to show every images listed in my DB (DB contains img name, img are stored in local files, each has been send in the same time via an input page).
The current code do not send any errors between thoses parts :
public function findall()
{
require_once ('Classes/ClasseDB.php');
$pdo = Database::connect();
$req = "SELECT IDphoto, nomImage, testCom FROM test";
$stmt = $pdo->query($req);
$CollectionPhotos = array();
while ($ligne = $stmt->fetch())
{
$LaPhoto = new ClasseTest($ligne["testCom"]);
array_push($CollectionPhotos, $LaPhoto);
}
return $CollectionPhotos;
}
public function get_nomImage()
{
return $this->nomImage;
}
And
Image List :
<?php
echo "test1 ";
require_once "Classes/ClasseTest.php";
$laPhoto = new ClasseTest;
$CollectionPhotos = $laPhoto -> findall();
$i = 0;
echo "test2 ";
while ($i < count($CollectionPhotos)){
// here is where it's broken ↓
echo $CollectionPhotos[$i]->get_nomImage(); //don't work :'(
//html <img __ > is removed in order to simplify
echo 'test3 '; //shows every items
$i++;
}
echo "test4 ";
?>
ClasseDB code here as asked :
<?php
class Database {
public static $conn = null;
public static function connect() {
if ( null == self::$conn ) {
try {
self::$conn = new PDO('mysql:host=localhost;dbname=myDB', 'root', '');
self::$conn->query("SET NAMES 'utf8'");
self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e) {
die($e->getMessage());
}
}
return self::$conn;
}
}
?>
Each tests are shown, and 'test3' appears 10 times, so i know my loop repeats enough times.
Nb : this is like what we did in class...
So what is wronnnng, is that a newbie mistake, a misspelling or a bug (my pc has done everything to me so that's also possible) ?
In your SQL query, you select three columns:
SELECT IDphoto, nomImage, testCom FROM test
However, only one of these is actually referenced in your PHP code:
$LaPhoto = new ClasseTest($ligne["testCom"]);
You haven't shown the constructor of ClasseTest, so it's not clear where testCom is stored (if anywhere), but from its name, I suspect it is not in the nomImage property, which is what you later try to retrieve:
public function get_nomImage()
{
return $this->nomImage;
}
That property is presumably supposed to come from the nomImage column in the database, which you're not storing anywhere.
You can help yourself a lot by naming things more carefully and thinking about responsibilities. If you want an object representing a photo, the logical name for it would be Photo, and you would pass all the columns you've selected to its constructor:
$LaPhoto = new Photo($ligne["IDphoto"], $ligne["nomImage"], $ligne["testCom"]);
Your findAll method doesn't belong to a single photo, so you can either put it on a different class, like ChercheurPhoto:
$leChercheurPhoto = new ChercheurPhoto;
$CollectionPhotos = $leChercheurPhoto->findall();
Or, you can use a static method, which is a method on the class, not a particular instance of the class:
$CollectionPhotos = Photo::findall();
Related
I'm having a problem with some PHP / MySQL code.
I need a view called gameview for a Star Wars game I'm writing.
If I created the view in MySQL then the code runs perfectly. However, I need this view to be dropped every time the game starts. So if I start without the view "gameview" present in the DB, the page cannot be displayed due to the view not existing. However, the moment I manually add the view into MySQL, it works. I can't see why.
Class code
<?php
class gameView
{
protected $Conn;
public function __construct($Conn)
{
$this->Conn = $Conn;
}
public function dropGameView()
{
$drop = "DROP VIEW if EXISTS gameview;";
$stmt = $this->Conn->prepare($drop);
$stmt->execute(array());
}
public function createGameView()
{
$view = "CREATE VIEW gameview AS SELECT id, name, image, quote FROM person;";
$stmt = $this->Conn->prepare($view);
$stmt->execute(array());
}
public function useGameView()
{
$query = "SELECT * from gameview";
$stmt = $this->Conn->prepare($query);
$stmt->execute(array());
$gameView = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $gameView;
}
}
?>
PHP code
<?php
$gameView = new gameView($Conn);
$finalCharacter = $gameView->useGameView();
$smarty->assign('game_view', $finalCharacter);
?>
Well.... stone the crows. I thought this would be too simple to work, but it did!
<?php
$gameView = new gameView($Conn);
$dropGameView = $gameView->dropGameView();
$smarty->assign('drop_gameview', $dropGameView);
$createGameView = $gameView->createGameView();
$smarty->assign('create_gameview', $createGameView);
$finalCharacter = $gameView->useGameView();
$smarty->assign('game_view', $finalCharacter);
?>
Now to crack on and use the view.
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 have two PHP scripts that I included below. Both of them attempt to do the same thing, but one works and one does not. I'm looking for someone to explain what PHP is doing under the covers. I'm new to PHP and I suspect that my Java experience is poisoning my thought process when I work in PHP.
What I'm attempting to do is functionally very simple -- Insert a question into a mySQL database table, retrieve the primary key of the inserted row, and then insert five answers into another table with a foreign key relationship to the question.
My original logic looked like this:
ManageQuestions.php:
<?php
session_start();
include('query.php');
echo "begin <br>";
if (isset($_POST['submit'])) {
echo "manageQuestion <br>";
$query = new Query;
$query->createTransaction();
$query->executeCreateUpdateDelete("INSERT INTO question (question) VALUES ('".$_POST['question']."'); ");
$question_pid = $query->getLastInsertedId();
$query->commitTransaction(); // Need to figure out how to do dirty reads so I can remove this.
echo $question_pid."<br>";
$result = $query->executeRead("SELECT question_pid FROM question where question_pid = '".$question_pid."';");
echo count($result)."<br>";
//if (count($result) === 1) {
$query->createTransaction(); // Need to figure out how to do dirty reads so I can remove this.
foreach($_POST['answer'] as $answer) {
$correctAnswers = 0;
$query->executeCreateUpdateDelete("INSERT INTO answer (question_fid, answer, isCorrect) VALUES ('".$question_pid."','".$answer['answer']."','".$answer['isCorrect']."')");
if ($answer['isCorrect'] === 1) {
$correctAnswers = $correctAnaswers + 1;
if ($correctAnswers > 1){
echo "Failed to insert answers";
$query->rollBackTransaction();
break;
}
}
}
echo "Success";
$query->commitTransaction();
/* } else {
echo "Failed to insert question";
$query->rollBackTransaction();
} */
}
?>
Query.php:
<?php
session_start();
class Query
{
private $host="<censored>";
private $username="<censored>";
private $password="<censored>";
private $db_name="<censored>";
private $pdo;
private $pdo_statement;
private $pdo_exception;
public function executeCreateUpdateDelete($pQuery)
{
$this->pdo_statement = $this->pdo->prepare($pQuery);
return $this->pdo_statement->execute();
}
public function executeRead($pQuery)
{
try
{
$dbh = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
$result = $dbh->query($pQuery);
$dbh = null;
return $result->fetchAll();
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
public function createTransaction()
{
$this->pdo = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
$this->pdo->beginTransaction();
}
public function commitTransaction()
{
$this->pdo->commit();
}
public function rollBackTransaction()
{
$this->pdo->rollBack();
}
public function getLastInsertedId()
{
$this->pdo->lastInsertId();
}
}
?>
When I rewrote my logic to not use a separate query class, I was able to do what I wanted to do. The only thing I've been able to find online about the life cycle of a PHP object is that it begins at the start of a script and ends at the end of a script. Does that imply that my query object is instantiated every time I call one of its methods and garbage collected when that particular method ends? Moving the logic out of that class and into the script caused my logic to work. This is what it looks like now:
ManageQuestions.php:
<?php
session_start();
include('query.php');
echo "Begin <br>";
if (isset($_POST['submit'])) {
echo "manageQuestion <br>";
$host="<censored>";
$username="<censored>";
$password="<censored>";
$db_name="<censored>";
$pdo = new PDO("mysql:host=$host;dbname=$db_name", $username, $password);
$stmt = $pdo->prepare("INSERT INTO question (question) VALUES ('".$_POST['question']."'); ");
$stmt->execute();
$question_pid = $pdo->lastInsertId();
echo $question_pid."<br>";
$stmt = $pdo->query("SELECT question_pid FROM question where question_pid = '".$question_pid."';");
$result = $stmt->fetchAll();
echo count($result)."<br>";
foreach($_POST['answer'] as $answer) {
$correctAnswers = 0;
$stmt = $pdo->prepare("INSERT INTO answer (question_fid, answer, isCorrect) VALUES ('".$question_pid."','".$answer['answer']."','".$answer['isCorrect']."')");
$stmt->execute();
}
echo "Success";
}
?>
Even though this fixed my issue, I don't understand why. If someone could explain that, I would be extremely grateful.
Cheers!
Does that imply that my query object is instantiated every time I call one of its methods and garbage collected when that particular method ends?
No. It's per request, not per method call. So the query object is instantiated every time the script is called and it gets unset (and not necessarily garbage collected) when the script ends.
However you could better manage the resource of the PDO object inside your Query class because you create a new instance (which would mean that it connects again to the database server which is not that cheap). So some lazy loading does not seem bad:
class Query
{
...
/** #var PDO */
private $pdo;
...
private function getPdo() {
if (!$this->pdo) {
$this->pdo = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
}
return $this->pdo;
}
public function executeRead($pQuery)
{
try {
$dbh = $this->getPdo();
$result = $dbh->query($pQuery);
return $result->fetchAll();
} catch (PDOException $e) {
echo $e->getMessage();
}
}
public function createTransaction()
{
$this->getPdo()->beginTransaction();
}
...
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.
PHP/MySQLisolating database access in class - how to handle multiple row Selects
Here’s a coding question.
I isolated all DB access functions in a class
<?php
class DB {
var $conn;
function DBClass () {
#$this-> conn = mysqli_connect (DB_SERVER, DB_USER, DB_PASS, DB_NAME);
}
function validateUser ($aUserid, $aPassword) {
… validation code – sql injection code etc..
$sql = "Select userid, name, level From users where userid = '$aUserid' and password = '$aPassword'";
$result = mysqli_query ( $this->conn, $sql );
if (!$result || (mysqli_num_rows ($result) < 1)) {
return false;
}
$dbarray = mysqli_fetch_assoc ($result); // get a row
return $dbarray;
}
function getProduct ($aProductid) {
return $dbarray;
}
function getProductList () {
// <----------- this would be the problem function
}
}
$DB = new DBClass();
?>
My calling routine:
<?php
$dbarray = $DB->validateUser ($_POST['userid'], $_POST['password']);
?>
No problem it works fine. I run into a problem with a result set of more than one row. Now I have to get back to the class object for each row. It’s no problem if I include the MySQL code in the calling routine, but I’d like to keep it isolated in my class and I’m not sure how to code it.
Any thoughts? Any examples?
If you use PHP 5.3.0 and mysqlnd, you can use the new function mysqli_fetch_all(). This returns an array of associative arrays.
If you use an earlier version of PHP, you could switch to using PDO, and use the function PDOStatement::fetchAll().
You ask in a comment what about a very large result set. It's true that an unbounded result set could cause the array to exceed your PHP memory limit and that would cause a fatal error and halt the script. But is this really a problem? How many products do you have? You could use LIMIT to make sure the query isn't unbounded.
Re the other part of your questions regarding going back to a class, I'd suggest making an Iterator class:
class DB implements IteratorAggregate
{
protected $_data = array();
public function getProductList() {
// fetch all results from SQL query, stuff them into $this->_data
return $this->getIterator();
}
public function getIterator() {
return new ArrayIterator($this->_data);
}
}
Now you can use the class in a foreach loop:
$db = new DB();
foreach ($db->getProductList() as $product) {
// do something with each product
}
The IteratorAggregate interface means you can even do this:
$db = new DB();
$db->getProductList();
// ...other steps...
foreach ($db as $product) {
// do something with each product
}
Of course you could only store one result set at a time with this method. If you used your DB class for any other queries in the meantime, it would complicate things. For this reason, most people don't try to write a single class to encapsulate all database operations. They write individual classes for each type of Domain Model they need to work with, decoupled from the database connection.
you could save the result in an array and return it:
function getProductList () {
$sql = "SELECT ...";
$result = mysqli_query ( $this->conn, $sql );
$myProducts = array();
while ($row = mysqli_fetch_assoc($result))
$myProducts[] = $row; // or array_push($myProducts, $row)
}
return $myProducts
}
As a result you'll have an array of arrays and each element of it will contain one row of the result.
You have a SQL injection right in your login page.
What happens if someone inputs that as password:
xxx' OR 'yyy' <> 'x