Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Jahuran
Jul 9, 2001
I'm having some trouble with a php mysql query / database layout I want to set up. Perhaps someone can point me in the right direction.

I have a main table that holds recipe information.

recipes
recipeID (INT auto_increment PK)
recipeName(VARCHAR)
recipeDescription
dateCreated
dateUpdated
userID

I have a table that holds ingredients

ingredients
ingredientID(INT auto_increment PK)
ingredientName(VARCHAR)
ingredientDescription(VARCHAR)

Then I have a table connecting different ingredients to a recipe

ingredientsRecipes
ingredientRecipeID
recipeID
ingredientID

I want to make a search query that allows me to say: Give me all the recipeIDs that contain eggs,flour,water, etc. However this would require me to query the same table columns multiple times. I'm not completely sure on how to set up a query like that.

Normally I'd say:
code:
SELECT * FROM `ingredientsRecipes` WHERE `ingredientID`='X' AND `ingredientID`='Y';
This wouldn't give me any results since there aren't any rows where the ingredientID is both X and Y.
Perhaps its really simple and I'm missing something. If my table setup is wrong please by all means correct me.

Adbot
ADBOT LOVES YOU

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

Jahuran posted:

I want to make a search query that allows me to say: Give me all the recipeIDs that contain eggs,flour,water, etc. However this would require me to query the same table columns multiple times. I'm not completely sure on how to set up a query like that.

code:
SELECT * FROM ingredientsRecipes WHERE ingredientID IN (23,32,34)

KuruMonkey
Jul 23, 2004

Jahuran posted:

I'm having some trouble with a php mysql query / database layout I want to set up. Perhaps someone can point me in the right direction.

...


I want to make a search query that allows me to say: Give me all the recipeIDs that contain eggs,flour,water, etc. However this would require me to query the same table columns multiple times. I'm not completely sure on how to set up a query like that.

Normally I'd say:
code:
SELECT * FROM `ingredientsRecipes` WHERE `ingredientID`='X' AND `ingredientID`='Y';

If Begby was correct, ignore this:

If I read you correctly, you want recipes with eggs AND flour AND water? In which case Begby's answer won't be helping you at all, as IN is a shorthand for OR.

What you want is subqueries:

SELECT recipeID FROM recipesIngredients WHERE ingredientID = 'X' AND recipeID IN (SELECT recipeID FROM recipesIngredients WHERE ingredientID = 'Y') AND recipedID IN (SELECT ...

Which basically lets you chain queries together, but have the SQL engine do the lifting for you still.

(caveat: not tested that syntax, but see http://dev.mysql.com/doc/refman/5.0/en/subqueries.html)

Edit: break those tables!
p.s. this was also really an SQL question, there are likely SQL goons that are better at SQL than us PHP goons.

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums
PHP msqli question.

Is there a non hack way to deal with dynamic mysqli prepared statements?

Seriously msqli_stmt::bind_param and the bind_result functions are the worst pieces of poo poo functions I have ever had to deal with, ever.

Golbez
Oct 9, 2002

1 2 3!
If you want to take a shot at me get in line, line
1 2 3!
Baby, I've had all my shots and I'm fine

haywire posted:

PHP msqli question.

Is there a non hack way to deal with dynamic mysqli prepared statements?

Seriously msqli_stmt::bind_param and the bind_result functions are the worst pieces of poo poo functions I have ever had to deal with, ever.

Can you be a little more specific? You mean like having a dynamic number of parameters in a statement?

Here's a function I found somewhere that I've been using, borrowed from http://www.devmorgan.com/blog/2009/03/27/dydl-part-3-dynamic-binding-with-mysqli-php/

php:
<?
public function bindParams($stmt,$params)
{
    if ($params != null) {
        $types = '';                        //initial sting with types
        foreach($params as $param) {        //for each element, determine type and add
            if(is_int($param)) {
                $types .= 'i';              //integer
            } elseif (is_float($param)) {
                $types .= 'd';              //double
            } elseif (is_string($param)) {
                $types .= 's';              //string
            } else {
                $types .= 'b';              //blob and unknown
            }
        }

        $bind_names[] = $types;             //first param needed is the type string
        // eg:  'issss'

        for ($i=0; $i<count($params);$i++) {//go through incoming params and added em to array
            $bind_name = 'bind' . $i;       //give them an arbitrary name
            $$bind_name = $params[$i];      //add the parameter to the variable variable
            $bind_names[] = &$$bind_name;   //now associate the variable as an element in an array
        }

        //call the function bind_param with dynamic params
        call_user_func_array(array($stmt,'bind_param'),$bind_names);
    }
    return $stmt;                           //return the bound statement 
}
?>
I put this in my class that extends mysqli, so I can use it like this:

php:
<?
$stmt = $mysqli->bindParams($mysqli->prepare($query),$queryParams);
?>
where $queryParams is an array of parameters. The function handles figuring out if it's an integer or string or what not, and binds things accordingly.

McGlockenshire
Dec 16, 2005

GOLLOCKS!

quote:

Is there a non hack way to deal with dynamic mysqli prepared statements?

Can't use PDO instead?

Jahuran
Jul 9, 2001

KuruMonkey posted:

If Begby was correct, ignore this:

If I read you correctly, you want recipes with eggs AND flour AND water? In which case Begby's answer won't be helping you at all, as IN is a shorthand for OR.

What you want is subqueries:

SELECT recipeID FROM recipesIngredients WHERE ingredientID = 'X' AND recipeID IN (SELECT recipeID FROM recipesIngredients WHERE ingredientID = 'Y') AND recipedID IN (SELECT ...

Which basically lets you chain queries together, but have the SQL engine do the lifting for you still.

(caveat: not tested that syntax, but see http://dev.mysql.com/doc/refman/5.0/en/subqueries.html)

Edit: break those tables!
p.s. this was also really an SQL question, there are likely SQL goons that are better at SQL than us PHP goons.

Thank you for the replies. I saw the thread about database questions so I figured my question would be better off there. Above suggestion with IN does work for me.

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Golbez posted:

Can you be a little more specific? You mean like having a dynamic number of parameters in a statement?

Here's a function I found somewhere that I've been using, borrowed from http://www.devmorgan.com/blog/2009/03/27/dydl-part-3-dynamic-binding-with-mysqli-php/

php:
<?
public function bindParams($stmt,$params)
{
    if ($params != null) {
        $types = '';                        //initial sting with types
        foreach($params as $param) {        //for each element, determine type and add
            if(is_int($param)) {
                $types .= 'i';              //integer
            } elseif (is_float($param)) {
                $types .= 'd';              //double
            } elseif (is_string($param)) {
                $types .= 's';              //string
            } else {
                $types .= 'b';              //blob and unknown
            }
        }

        $bind_names[] = $types;             //first param needed is the type string
        // eg:  'issss'

        for ($i=0; $i<count($params);$i++) {//go through incoming params and added em to array
            $bind_name = 'bind' . $i;       //give them an arbitrary name
            $$bind_name = $params[$i];      //add the parameter to the variable variable
            $bind_names[] = &$$bind_name;   //now associate the variable as an element in an array
        }

        //call the function bind_param with dynamic params
        call_user_func_array(array($stmt,'bind_param'),$bind_names);
    }
    return $stmt;                           //return the bound statement 
}
?>
I put this in my class that extends mysqli, so I can use it like this:

php:
<?
$stmt = $mysqli->bindParams($mysqli->prepare($query),$queryParams);
?>
where $queryParams is an array of parameters. The function handles figuring out if it's an integer or string or what not, and binds things accordingly.


I think that is pretty much the money, but for small queries, is doing all that code going to be slowing me down? I'm trying to write a query that would get a list of about 10 items every second (it generates json). It needs to be really fast.

How would you manage the output? (IE making a function that returned an array like in fetch_array or PDO's fetchAll)

McGlockenshire posted:

Can't use PDO instead?

I read somewhere that PDO was significantly slower than mysqli/mysql. If I'm wrong, please say. I really like PDO but this is going to need to be as fast as possible.

Also, doesn't PDO have pretty much the same issue?

Rat Supremacy fucked around with this message at 18:25 on Aug 8, 2009

Mr Viper
Jun 21, 2005

Derezzed...
Does anyone know of a guide to successfully installing phpMyAdmin? I've tried it so many times and literally never gotten it to work.

I downloaded the .zip and unzipped it.

Then I added a config.inc.php file. My hosting company, 1and1, has a guide on how to edit it to configure with their mysql databases. My ultimate goal is to put all of my databases (from different servers) onto the same phpmyadmin page, so I don't have to constantly be going from one to another on 1and1's annoying control panel.

Here's the contents of the config.inc.php file. The all caps stuff obviously has the correct host/username/pass for my mysql database.
php:
<?
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
 * phpMyAdmin sample configuration, you can use it as base for
 * manual configuration. For easier setup you can use setup/
 *
 * All directives are explained in Documentation.html and on phpMyAdmin
 * wiki <[url]http://wiki.cihar.com[/url]>.
 *
 * @version $Id: config.sample.inc.php 11781 2008-11-06 05:29:28Z rajkissu $
 */

/*
 * This is needed for cookie based authentication to encrypt password in
 * cookie
 */
$cfg['blowfish_secret'] = ''; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */

/*
 * Servers configuration
 */
$i = 0;

/*
 * First server
 */
$i++;
/* Authentication type */
$cfg['Servers'][$i]['auth_type']     = 'config';
$cfg['Servers'][$i]['user']          = 'MYSQL_DATABASE_USERNAME';
$cfg['Servers'][$i]['password']      = 'MYSQL_DATABASE_PASSWORD';
$cfg['Servers'][$i]['only_db']       = 'MYSQL_DATABASE_DB_NAME';

/* Server parameters */
$cfg['Servers'][$i]['host'] = 'MYSQL_DATABASE_HOST';
$cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['compress'] = false;
/* Select mysqli if your server has it */
$cfg['Servers'][$i]['extension'] = 'mysql';

/* rajk - for blobstreaming */
$cfg['Servers'][$i]['bs_garbage_threshold'] = 50;
$cfg['Servers'][$i]['bs_repository_threshold'] = '32M';
$cfg['Servers'][$i]['bs_temp_blob_timeout'] = 600;
$cfg['Servers'][$i]['bs_temp_log_threshold'] = '32M';

/* User for advanced features */
// $cfg['Servers'][$i]['controluser'] = 'pma';
// $cfg['Servers'][$i]['controlpass'] = 'pmapass';
/* Advanced phpMyAdmin features */
// $cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
// $cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
// $cfg['Servers'][$i]['relation'] = 'pma_relation';
// $cfg['Servers'][$i]['table_info'] = 'pma_table_info';
// $cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
// $cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
// $cfg['Servers'][$i]['column_info'] = 'pma_column_info';
// $cfg['Servers'][$i]['history'] = 'pma_history';
// $cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';
/* Contrib / Swekey authentication */
// $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf';

/*
 * End of servers configuration
 */

/*
 * Directories for saving/loading files from server
 */
$cfg['UploadDir'] = '';
$cfg['SaveDir'] = '';
?>
When I upload everything to mywebsite.com/phpmyadmin, I get the strangest error:



What the hell does that even mean?



If I leave the config.inc.php file the way it was when I unzipped the whole package, it just goes to a login screen, and asks for a username and password that I don't have (or know where to find).


I really wish this would work, it would save me so much time, but it really never has.

McGlockenshire
Dec 16, 2005

GOLLOCKS!

haywire posted:

I read somewhere that PDO was significantly slower than mysqli/mysql. If I'm wrong, please say. I really like PDO but this is going to need to be as fast as possible.
Unless you're making tens of thousands of queries in a single script run and need it NOW NOW NOW NOW NOW NOW NOW you won't notice any slowdown. I've never noticed any performance difference. Even then, if performance is what you're looking for, why are you working with PHP? ;)

quote:

Also, doesn't PDO have pretty much the same issue?
What issue? You have a query, you have the placeholders, you have a list of stuff to replace the placeholders with. Maybe I don't really understand what problem you're encountering...

Mr Viper posted:

Does anyone know of a guide to successfully installing phpMyAdmin?
1) Unzip.
2) Use the included configuration utility (in like /setup/ or /configuration/ or /install/ or something like that) to create your config.inc.php file
3) Copy the created file into the right place and nuke the setup directory
4) Works!
But what you have now looks like it SHOULD work...

But

quote:

this is hilarious. Google suggests that you're either hosted on Windows using a MySQL 4.1 Alpha, or your host's configuration is excessively hosed up...

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

McGlockenshire posted:

Unless you're making tens of thousands of queries in a single script run and need it NOW NOW NOW NOW NOW NOW NOW you won't notice any slowdown. I've never noticed any performance difference. Even then, if performance is what you're looking for, why are you working with PHP? ;)

The issue isn't speed AS such but the results are time critical (auctions being bid), and it needs to be able to handle lots of requests.

quote:

What issue? You have a query, you have the placeholders, you have a list of stuff to replace the placeholders with. Maybe I don't really understand what problem you're encountering...

Having a variable number of placeholders. IE for a select where IN( ?, ? ) query, but there could be lots of question marks.

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

haywire posted:

Having a variable number of placeholders. IE for a select where IN( ?, ? ) query, but there could be lots of question marks.

In that case you have to create the select clause dynamically. If there are 10 items that go in the clause, then you add 10 questions marks to the string.

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Begby posted:

In that case you have to create the select clause dynamically. If there are 10 items that go in the clause, then you add 10 questions marks to the string.


Yeah that part isn't hard, it is the binding of those parameters to things. I don't think PDO has this issue, on reflection. mysqli_stmt::bind_param has the most loving retarded syntax ever.

McGlockenshire
Dec 16, 2005

GOLLOCKS!

haywire posted:

Yeah that part isn't hard, it is the binding of those parameters to things. I don't think PDO has this issue, on reflection. mysqli_stmt::bind_param has the most loving retarded syntax ever.

Ah, yes, now I understand.

Unfortunately I don't know of anything short of a custom solution that fixes the problem. It was one of the things that drove me up the wall when switching to PHP from Perl. With DBI, you can just pass in an array reference, and it'll automatically understand to use that for one and only one placeholder...

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

McGlockenshire posted:

Ah, yes, now I understand.

Unfortunately I don't know of anything short of a custom solution that fixes the problem. It was one of the things that drove me up the wall when switching to PHP from Perl. With DBI, you can just pass in an array reference, and it'll automatically understand to use that for one and only one placeholder...

Yeah, it is just that one, poorly written function. I expect you could hack up the extension quite easily, but I don't want my code to only work on modded install.

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

haywire posted:

Yeah, it is just that one, poorly written function. I expect you could hack up the extension quite easily, but I don't want my code to only work on modded install.

You can extend PDO quite easily without hacking the actual install. You just include your PDO class with your code.

Another option is to look at Zend_DB from Zend Framework. Overall I don't like the framework too much, but one thing its got going is that all the parts are completely independent, so you can just use the Zend_DB part and ignore the rest. Its really quite simple to use.

Also, it includes a mysqli and a pdo adapter. So you can write all your code to use Zend_DB, then switch between mysqli or pdo with a simple config change and not have to modify your code.

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Begby posted:

You can extend PDO quite easily without hacking the actual install. You just include your PDO class with your code.

Another option is to look at Zend_DB from Zend Framework. Overall I don't like the framework too much, but one thing its got going is that all the parts are completely independent, so you can just use the Zend_DB part and ignore the rest. Its really quite simple to use.

Also, it includes a mysqli and a pdo adapter. So you can write all your code to use Zend_DB, then switch between mysqli or pdo with a simple config change and not have to modify your code.

I meant extending mysqli, and I don't think my extension of the mysqli class quite has the capability. I want to avoid using too much 3rd party stuff, too. So I'll just have to do some independant PDO vs mysqli research, or look at other ways at speeding things up, or just stick with straight up queries for some things.

LuckySevens
Feb 16, 2004

fear not failure, fear only the limitations of our dreams

Here's a simple newbie question, I'm doing a wordpress blog at the moment and everything has gone smoothly with redesigning the format, css, and getting it to do what I want.

The last thing I'm looking to do is automatically post a small stock image in the top left corner of the blog post. The image posted will depend on the wordpress category i'm using.

code:
<div class="post">

<h3><?php the_title(); ?><?php edit_post_link('Edit', '<span class="edit">', '</span>'); ?></h3>

<p class="byline">In <?php the_category(', ') ?> on <?php the_time('j F Y') ?> <?php the_tags('tagged ', ', ', ''); ?> 
with <?php comments_number('no comments', '1 comment', '% comments'); ?></p>

<?php the_content(); ?>

<br />
This is the code, I know I can probably use an if statement and link it with what category the blog post is in which will choose what image is displayed, but how would I go about this? And how should I format it so the blogpost autowraps around the image?

LuckySevens fucked around with this message at 08:20 on Aug 11, 2009

Treytor
Feb 8, 2003

Enjoy, uh... refreshing time!
Is there an easy way to mask the source of a download link? For example, I have an file at http://192.168.0.1/soundfile.mp3, but I want it to appear to be coming from http://www.example.com/soundfile.mp3

Can this be done easily?

Thanks

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Treytor posted:

Is there an easy way to mask the source of a download link? For example, I have an file at http://192.168.0.1/soundfile.mp3, but I want it to appear to be coming from http://www.example.com/soundfile.mp3

Can this be done easily?

Thanks

Do you own https://www.example.com

If so, you'd put a file on there that gets the http://192.168.0.1/soundfile.mp3 (assuming that it is actually a web accessible IP and not a lan one) and builds the mime header, then shoves the contents to the client.

Treytor
Feb 8, 2003

Enjoy, uh... refreshing time!
Yes you are correct on your assumptions. I guess I just have no idea how to do what you're saying, but it's promising to hear that it is possible.

LuckySevens
Feb 16, 2004

fear not failure, fear only the limitations of our dreams

Actually I change my question, I managed to find a wp plugin which does the job nicely, the only problem now is getting the category image to format nicely with the blog post text. When I put the functions together:

code:
  <?php if (function_exists('get_cat_icon')) get_cat_icon("class=myicons"); ?>

<?php  the_content('[Read more &rarr;]');  ?> 
On the same line, it just places the image above the blog text. I'd like it to do a nice text wrap around for the get_cat_icon() image, I've tried to play around with the CSS but no luck, I've tried doing a few mods to how the plug in posts the <img> tags but no luck, I assume its some basic php knowledge that I'm missing that stops me from doing this.

Edit: In case its needed, documentation on the function from the plugin here

LuckySevens fucked around with this message at 13:22 on Aug 11, 2009

Supervillin
Feb 6, 2005

Pillbug

Treytor posted:

Yes you are correct on your assumptions. I guess I just have no idea how to do what you're saying, but it's promising to hear that it is possible.

192.168 sounds like an internal IP, not external, which means unless https://www.example.com is on that same network this will probably not work as you intend. BUT:

For default browser handling, whatever that might be (streaming, download, etc):
php:
<?
// set the header
header('Content-Type: audio/mpeg');

// prepare the request
$ch = curl_init('http://192.168.0.1/example.mp3');

// make the request
curl_exec($ch);

// cleanup
curl_close($ch);
?>
If you want to force the file to be downloaded, change the first line to header('Content-Type: application/octet-stream');

Now, if you name the above file example.mp3 then you have to tell PHP to process MP3 files. The easier way to do this is just point it to example.php. If it needs to be .mp3, then you'll have to use an .htaccess file to tell your server to process it as PHP. How to do that depends on how your server is set up, there's no one solution.

Supervillin fucked around with this message at 16:25 on Aug 11, 2009

Treytor
Feb 8, 2003

Enjoy, uh... refreshing time!
Thanks again for the help, I really appreciate it. The two servers in question here are not on the same network, but are both web accessible. I don't want my end user to be aware of the IP address server, only the domain name.

Golbez
Oct 9, 2002

1 2 3!
If you want to take a shot at me get in line, line
1 2 3!
Baby, I've had all my shots and I'm fine

haywire posted:

I think that is pretty much the money, but for small queries, is doing all that code going to be slowing me down? I'm trying to write a query that would get a list of about 10 items every second (it generates json). It needs to be really fast.

How would you manage the output? (IE making a function that returned an array like in fetch_array or PDO's fetchAll)

The output is managed like any other output, we're talking about a dynamic number of variables, not a dynamic number of selected columns...

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Golbez posted:

The output is managed like any other output, we're talking about a dynamic number of variables, not a dynamic number of selected columns...


Oh, I think I see, that does make sense. Doesn't change the params issue, thought :(


Anyone got suggestions on the fluent interfacing query generation thing? I want to do something like
php:
<?
while( $row = $db->select("hi","bye")->from("sup","hmoo")->where("blah","blah") )
{
 echo $row['hi'];
}
?>
And it be done as prepared statements.

Golbez
Oct 9, 2002

1 2 3!
If you want to take a shot at me get in line, line
1 2 3!
Baby, I've had all my shots and I'm fine

haywire posted:

Oh, I think I see, that does make sense. Doesn't change the params issue, thought :(
I'm pretty sure any params issue you have, I've confronted in the past. What issue, you mean the fact that you have to use that big function just to have a dynamic number of params, or ...? Share the issue, we'll work it out :) Show the query you want generated and we'll figure how to get there.

quote:

Anyone got suggestions on the fluent interfacing query generation thing? I want to do something like
php:
<?
while( $row = $db->select("hi","bye")->from("sup","hmoo")->where("blah","blah") )
{
 echo $row['hi'];
}
?>
And it be done as prepared statements.

I know I've seen that done somewhere but I couldn't tell you where off the top of my head.

Personally, I have no use for it, I hate abstraction and love working with the actual queries, though actually some of these things look very useful, like using an associative array for a mass insert in Zend_DB... but on the other hand, I figure, if you're putting it into the array, you can put it into the query. Same effort, and in my opinion, a tiny bit easier to debug.

It would be nice to be able to use this kind of thing for a dynamic number of parameters though, of course. It's really annoying you can't just repeat $mysqli->bind_param for each parameter. (And, for that matter, using an abstraction layer to repeatedly bind new "WHERE"s, so I wouldn't have to care about if "AND" or "WHERE" was used correctly... anyone know anything like that? :)

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Golbez posted:

I'm pretty sure any params issue you have, I've confronted in the past. What issue, you mean the fact that you have to use that big function just to have a dynamic number of params, or ...? Share the issue, we'll work it out :) Show the query you want generated and we'll figure how to get there.


I know I've seen that done somewhere but I couldn't tell you where off the top of my head.

Personally, I have no use for it, I hate abstraction and love working with the actual queries, though actually some of these things look very useful, like using an associative array for a mass insert in Zend_DB... but on the other hand, I figure, if you're putting it into the array, you can put it into the query. Same effort, and in my opinion, a tiny bit easier to debug.

It would be nice to be able to use this kind of thing for a dynamic number of parameters though, of course. It's really annoying you can't just repeat $mysqli->bind_param for each parameter. (And, for that matter, using an abstraction layer to repeatedly bind new "WHERE"s, so I wouldn't have to care about if "AND" or "WHERE" was used correctly... anyone know anything like that? :)


Dynamic # of parameters is exactly what I meant in my exact post.

Say you are getting a string in the format 1,6,3,5,7 from your GET. This can be any amount long. You want to get this as a prepared statement. You can easily make the query using implode or whatever and substituting the numbers with ?. However, you have to use the loving bindParam syntax which is utterly nasty in every shape and form.

Golbez
Oct 9, 2002

1 2 3!
If you want to take a shot at me get in line, line
1 2 3!
Baby, I've had all my shots and I'm fine

haywire posted:

Dynamic # of parameters is exactly what I meant in my exact post.

Say you are getting a string in the format 1,6,3,5,7 from your GET. This can be any amount long. You want to get this as a prepared statement. You can easily make the query using implode or whatever and substituting the numbers with ?. However, you have to use the loving bindParam syntax which is utterly nasty in every shape and form.

Yes, it is. But there are ways around it. Make a function that, for every entry in an array, adds a "?," to your query (then trim the last comma, or do whatever feels right), then use the function I gave above to process a dynamic number of incoming parameters.

So you have $query = "SELECT * FROM table WHERE RowID IN ($questionmarks)", $questionmarks consisting of "?,?,?,?,?", $values being an array consisting of 1, 6, 3, 5, and 7, and running that through the bindParams function earlier.

It's not pretty, but it gets the job done.

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

haywire posted:

Oh, I think I see, that does make sense. Doesn't change the params issue, thought :(


Anyone got suggestions on the fluent interfacing query generation thing? I want to do something like
php:
<?
while( $row = $db->select("hi","bye")->from("sup","hmoo")->where("blah","blah") )
{
 echo $row['hi'];
}
?>
And it be done as prepared statements.

You can do the fluent interface thing with Zend_DB using mysqli as the driver and it will use prepared statements (Also, Zend_DB doesn't saddle you with the gay rear end mysqli::bind_param bullshit).

The problems you are having really aren't that big of a deal and you can solve them most by using Zend_DB, extending Zend_DB, extending PDO, or rolling your own mysqli wrapper.

Writing a fluent interface on your own isn't too difficult, you have an object return itself, the methods each write to arrays in the object, then the execute() method you take all the crap from the arrays and create a fancy SQL statement. Making sure the AND/WHERES get sorted out is as simple as doing implode(' AND ', $valuepairs) and sticking a WHERE in front. Creating the question marks for IN is as simple as doing implode(',', $arrayOfQuestionMarks)

A fluent interface can be done by returning a self reference
code:
class MySuperPimpDBThing()
{
   private $fields = array();
   private $tables = array();
   private $wherePiecesParts = array();

   function Select($fields)
   {
      // add the fields to the array
      return $this;
   }
   
   function Where($field, $value)
   {
      $wherePiecesParts[] = array($field, $value);
      return $this;
   }

   function WhereIn($field, $arrayOfValues)
   {
     // yada yada
     return this;
   }
   
   function __toString()
   {
      // Get the prepared sql statement with place holders
   }

   function GetValuePairs()
   {
      // return an array of all your values indexed by the place holders
      // or in the proper order matching your sql statment
   }

   function execute()
   {
      // This is where all the magic happens
      // use __toString() and GetValuePairs() to build and execute the 
      // query using your api of choice
   }
}
To keep it simple I would just write a thing that generates your where clause, its probably easier just to write out your own join/select SQL.

You should really check out Zend_DB for ideas at the very least. Give PDO a try too. Yeah, its a bit slower, but I don't think its going to be noticeably slower even under heavy load. We use it on a database with about 30 million records under pretty high load where I work. Personally if it came down to it getting slow, I would much rather buy faster hardware than spend the time and headaches to move to mysqli.

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Begby posted:

You can do the fluent interface thing with Zend_DB using mysqli as the driver and it will use prepared statements (Also, Zend_DB doesn't saddle you with the gay rear end mysqli::bind_param bullshit).

The problems you are having really aren't that big of a deal and you can solve them most by using Zend_DB, extending Zend_DB, extending PDO, or rolling your own mysqli wrapper.

Writing a fluent interface on your own isn't too difficult, you have an object return itself, the methods each write to arrays in the object, then the execute() method you take all the crap from the arrays and create a fancy SQL statement. Making sure the AND/WHERES get sorted out is as simple as doing implode(' AND ', $valuepairs) and sticking a WHERE in front. Creating the question marks for IN is as simple as doing implode(',', $arrayOfQuestionMarks)

A fluent interface can be done by returning a self reference
code:
class MySuperPimpDBThing()
{
   private $fields = array();
   private $tables = array();
   private $wherePiecesParts = array();

   function Select($fields)
   {
      // add the fields to the array
      return $this;
   }
   
   function Where($field, $value)
   {
      $wherePiecesParts[] = array($field, $value);
      return $this;
   }

   function WhereIn($field, $arrayOfValues)
   {
     // yada yada
     return this;
   }
   
   function __toString()
   {
      // Get the prepared sql statement with place holders
   }

   function GetValuePairs()
   {
      // return an array of all your values indexed by the place holders
      // or in the proper order matching your sql statment
   }

   function execute()
   {
      // This is where all the magic happens
      // use __toString() and GetValuePairs() to build and execute the 
      // query using your api of choice
   }
}
To keep it simple I would just write a thing that generates your where clause, its probably easier just to write out your own join/select SQL.

You should really check out Zend_DB for ideas at the very least. Give PDO a try too. Yeah, its a bit slower, but I don't think its going to be noticeably slower even under heavy load. We use it on a database with about 30 million records under pretty high load where I work. Personally if it came down to it getting slow, I would much rather buy faster hardware than spend the time and headaches to move to mysqli.


I see what you mean, I do all of this already in this class:

http://github.com/radiosilence/cheapo/blob/ddbea0c21badb639b6fd83dc5ecc85e26f6a791f/cheapo/query.php

But I'm looking at making it an extension of db class or something to make using it less irritating.

I presume Zend_DB is part of the Zend framework? If so, do I have to use Zend framework in order to use Zend DB?

What do people think of out the Zend framework in general?

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

haywire posted:

I see what you mean, I do all of this already in this class:

http://github.com/radiosilence/cheapo/blob/ddbea0c21badb639b6fd83dc5ecc85e26f6a791f/cheapo/query.php

But I'm looking at making it an extension of db class or something to make using it less irritating.

I presume Zend_DB is part of the Zend framework? If so, do I have to use Zend framework in order to use Zend DB?

What do people think of out the Zend framework in general?

Overall I am really not that happy with a lot of the zend framework stuff. However, one thing they did very right was to make it so there is very little interdependency between the modules. So if you want to use Zend_DB, all you have to do is include that one thing in your project, you don't have to use anything else in their framework. (a lot of other frameworks require you to use all their crap) I think you could go so far as to delete everything except the Zend_DB folder and be good to go. (and by the way I already told you this about 15 posts back)

I use Zend_DB within codeigniter for a lot of stuff with no ill effects.

Zend_DB has a fluent interface. I don't use it though, all I use is an extension of the Zend_DB_Adapter class which I added a few minor odds and ends to.

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Begby posted:

Overall I am really not that happy with a lot of the zend framework stuff. However, one thing they did very right was to make it so there is very little interdependency between the modules. So if you want to use Zend_DB, all you have to do is include that one thing in your project, you don't have to use anything else in their framework. (a lot of other frameworks require you to use all their crap) I think you could go so far as to delete everything except the Zend_DB folder and be good to go. (and by the way I already told you this about 15 posts back)

I use Zend_DB within codeigniter for a lot of stuff with no ill effects.

Zend_DB has a fluent interface. I don't use it though, all I use is an extension of the Zend_DB_Adapter class which I added a few minor odds and ends to.

I'll definitely look into it.

I've got usage of my query fluent interface down to this:

php:
<?
        $db->build_query()
            ->select( "id" )
            ->from( "auctions" )
            ->where( "a", 1 )
            ->order( 'tl', 'asc' )
            ->limit( 8 );
        $db->do_query( $result );?>
Clean, in my opinion.

A Flaming Chicken
Feb 4, 2007
PHPLinq can do that - http://www.codeplex.com/PHPLinq

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

A Flaming Chicken posted:

PHPLinq can do that - http://www.codeplex.com/PHPLinq

That looks nice. I want to write my own though, that is why I am a little negative towards sessions of Zend. I'm hand rolling every part of my project (apart from the FirePHP debug class and jQuery) as I learn more that way and know exactly how to fix things.

Also I'm adverse to including masses of files and 3rd party things every run because inclusions really slow things down without caching.

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

haywire posted:

That looks nice. I want to write my own though, that is why I am a little negative towards sessions of Zend. I'm hand rolling every part of my project (apart from the FirePHP debug class and jQuery) as I learn more that way and know exactly how to fix things.

Also I'm adverse to including masses of files and 3rd party things every run because inclusions really slow things down without caching.

Its good to learn it. However, I think in the long run you should ditch the whole fluent interface for SQL thing. There is a reason why a lot of people don't use that, its nifty and slick, but inflexible.

In the time it takes you to write out the all the code to create a query in a fluent interface you could have written it in raw SQL. Then there will be queries where you have to go tinker with your class, like if you decide you need to do a subquery or a complicated AND clause requiring a lot of parenthesis.

You are better off trying to encapsulate all your database stuff into a set of classes that act as gateways for domain objects to your database with well written SQL instead of trying to code a sweet swiss army knife of sql.

Spaceguns
Aug 28, 2007

I am sorry to just drop this here but it has been a very frustraiting evening and I thought this was working earlier.

I am trying to get a form to email using php and oddly enough it works fine in IE and does nothing in firefox. I have even drilled it down to making the form painfully simple

Form code on one page
code:
<!-- form -->
<form method="POST" action="mailer.php">
<textarea rows="10" cols="50" name="comments"></textarea>
<input type="submit" name="submit" value="Submit" />
</form>
<!-- end form -->
In body of mailer.php page
email-address = an actual email address. The forums seems to be adding things for me.
code:
<?PHP
$email=$HTTP_POST_VARS;
$mailto="email-address"; //Your email address goes here.
$mailsubj="RSVP"; //Add your subject line here.
$mailhead="from: email-address\n";
reset($HTTP_POST_VARS);
$mailbody="Values submitted from website form:\n";
while(list($key,$val)=each($HTTP_POST_VARS))
{
$mailbody.="$key:$val\n";
}
mail($mailto,$mailsubj,$mailbody,$mailhead);
?>
It is probably something obvious but I have screwed around and tried different solutions and now I am probably missing the obvious. Help is appreciated as this is the last thing left to fix that I have identified.

edit - forums adding email tags

Spaceguns fucked around with this message at 06:00 on Aug 13, 2009

KuruMonkey
Jul 23, 2004
If it works in one browser and not another then the issue has to be in the rendered html of the form. It can't be in your form handling/emailing code as thats not run in the browser.

I'd suggest you replace mailer.php temporarily with a version that just var_dump()s $_POST to screen, and run the form through both browsers - check what the output of our temp page is, because one is probably giving garbage.

Edit: and make sure the server is displaying errors while doing this.

Then run your form page through an html / xhtml validator as appropriate, as it might help you find the issue faster. Its probably something really silly like an unclosed tag higher up in the page. Especially if IE is happy but FF shits it out.

Rat Supremacy
Jul 15, 2007

The custom title is an image and/or line of text that appears below your name in the forums

Begby posted:

Its good to learn it. However, I think in the long run you should ditch the whole fluent interface for SQL thing. There is a reason why a lot of people don't use that, its nifty and slick, but inflexible.

In the time it takes you to write out the all the code to create a query in a fluent interface you could have written it in raw SQL. Then there will be queries where you have to go tinker with your class, like if you decide you need to do a subquery or a complicated AND clause requiring a lot of parenthesis.

You are better off trying to encapsulate all your database stuff into a set of classes that act as gateways for domain objects to your database with well written SQL instead of trying to code a sweet swiss army knife of sql.


I've been wanting to implement django style models for a while now (manage.py sync-db, etc) but it is quite an odd issue..

I did it (sort of) in my first evar framework but it felt bloated.

The models (or "metaclasses" as I called them) looked something like this:

php:
<?php

/**
 * user.metaclass.php
 *
 * @version $Id$
 * @copyright 2007
 */

/**
 *
 *
 */
class user extends metaclass {
    /**
     * Constructor
     * @access protected
     */

     /* Metaclass Property Setup */

    public static $title "User";
    public static $table "testUsers";
    public static $propertyDefinitions = array(
        'username' => array(
            'type' => 'alphanumeric',
            'title' => 'Username',
            'requirements' => array(
                'exists' => true,
                'unique' => true,
            )
        ),
        'displayname' => array(
            'type' => 'string',
            'title' => 'Display Name',
        ),
        'registrationDate' => array(
            'type' => 'date',
            'title' => 'Registration Date',
            'hidden' => 'true',
        ),
        'password' => array(
            'type' => 'password',
            'title' => 'Password',
            'requirements' => array(
                'exists' => true,
            ),
        ),
        'email' => array(
            'type' => 'string',
            'title' => 'E-mail',
            'requirements' => array(
                'exists' => true,
            ),
        ),
        'group' => array(
            'type' => 'foreign',
            'requirements' => array(
                'exists' => true,
                'real' => 'group',
            ),
        ),
    );

    /* Start of Functions */
    public function setPassword($newPassword,$oldPassword=null)
    {
        if($oldPassword)
        {
            if(janitor::createPasswd($oldPassword,md5($this->property['registrationDate'])) !== $this->property['password'])
            {
                throw new Exception("oldpassworddoesnotmatch");
            }
        }

        $this->setProperty('password',janitor::createPasswd($newPassword,md5($this->property['registrationDate'])));
    }

    public function registerNewUser ()
    {

    }
}
?>
And I had a big ole' class called metaclass:

php:
<?php

/**
* metaclass.class.php
*
* @version $Id$
* @author James Cleveland
* @copyright 2007
*
* The idea is that every class has a database table, and this class handles
* putting things into and out of the database, creating the database needed
* generating queries, etc.
* Not that it should be used for every part of the system, just most of it.
* It is built using php's existing class system, for speed.
*/

abstract class metaclass
{
    public static $classesLoaded = array();
    public static $classesSkipped = array();
    public static $db;
    public static $tablesLoaded = array();
    public $invalid;
    private $id;
    private $properties;

    public static function init($db)
    {
        self :: $db $db;
    }

    public static function loadClass($class$rel null)
    {
        $classString = ($rel $rel.'/' null).$class;
        if (!(in_array($classStringmetaclass :: $classesSkipped) !== false))
        {
            if (!(in_array($classStringmetaclass :: $classesLoaded) !== false))
            {
                $siteMetaPath config :: get('site/path').'/metaclasses/'. ($rel $rel.'/' null).$class.'.class.php';
                $quickboxMetaPath config :: get('quickbox/path').'/metaclasses/'. ($rel $rel.'/' null).$class.'.class.php';
                $path file_exists($siteMetaPath) ? $siteMetaPath $quickboxMetaPath;
                if (file_exists($path))
                {
                    require_once $path;
                    metaclass :: $classesLoaded[] = $classString;
                }
                else
                {
                    metaclass :: $classesSkipped[] = $classString;
                }
            }
        }
    }

    public static function create($class$rel null)
    {
        $classString = ($rel $rel.'/' null).$class;
        self :: loadClass($class$rel);
        if (!in_array($classStringmetaclass :: $classesSkipped))
        {
            return new $class;
        }
        else
        {
            return false;
        }
    }

    public static function load($class$id)
    {
        $object metaclass :: create($class);
        $classVars metaclass :: getClassVars($class);
        $result self :: $db -> query("SELECT * FROM `".$classVars['table']."` WHERE `id` = '$id' LIMIT 1");
        $row self :: $db -> assoc($result);

        $object -> setId($id);
        foreach ($classVars['propertyDefinitions'] as $k => $v)
        {
            $object -> setProperty($k$row[$k]);
        }
        return $object;
    }

    public function save()
    {
        $classVars metaclass :: getClassVars(get_class($this));

        try {
            # Validation bit
            $validate = new validate(metaclass :: $db);
            $validate -> exec($this);

            if (!$validate -> valid)
            {
                $this -> invalid $validate -> invalid;
                return false;
            }
            else
            {
                if (!in_array($classVars['id'], self :: $tablesLoaded))
                {
                    if (!metaclass :: $db -> tableExists($classVars['table']))
                    {
                        metaclass::createTable($classVars['table'], $classVars['propertyDefinitions']);
                    }
                    metaclass::$tablesLoaded[] = $classVars['table'];
                }

                if (janitor::notNull($this -> id))
                {
                    # Inserting new row, creating object.
                    $query.= "UPDATE `".metaclass::$db -> getProperty('database')."`.`".$classVars['table']."`\n";
                    $query.= "SET\n";
                    foreach ($classVars['propertyDefinitions'] as $k => $v)
                    {
                        $queryRows[].= '`'.$k.'` = \''.janitor::cleanData($this -> properties[$k]).'\'';
                    }
                    $query.= implode(",\n"$queryRows);
                    $query.= "\nWHERE `".$classVars['table']."`.`id` = '".janitor::cleanData($this -> id)."'";
                    if (!metaclass::$db -> query($query))
                    {
                        return false;
                    }
                }
                else
                {
                    # Updating existing row, editing object.
                    $query.= "INSERT INTO `".metaclass::$db -> getProperty('database')."`.`".$classVars['table']."`\n";
                    foreach ($this -> properties as $k => $v)
                    {
                        $queryFields[].= '`'.$k.'`';
                        $queryData[].= '\''.janitor::cleanData($v).'\'';
                    }
                    $query.= "(".implode(",\n"$queryFields).")\n";
                    $query.= "VALUES\n (".implode(",\n"$queryData).")";
                    if (!metaclass::$db -> query($query))
                    {
                        return false;
                    }
                }
                return true;
            }
        }
        catch(Exception $e)
        {
            trigger_error($e -> getMessage(), E_USER_ERROR);
        }
    }

    public static function getClassVars($class)
    {
        self::loadClass($class);
        return get_class_vars($class);
    }

    private static function createTable($tableName$propertyDefinitions)
    {

        $query.= " CREATE TABLE `".metaclass::$db -> getProperty('database')."`.`$tableName` (\n";
        $query.= " `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,\n";
        foreach ($propertyDefinitions as $k => $v)
        {
            $vars metaclass::getClassVars($v['type']);
            $querylines[].= "`$k` ". (janitor::notNull($vars['fieldtype']) ? $vars['fieldtype'] : 'VARCHAR( 255 )')." NOT NULL ";
        }

        $query.= implode(",\n"$querylines);
        $query.= ")";
        metaclass::$db -> query($query);

    }

    public function setProperty($property$value)
    {
        $this -> properties[$property] = $value;
    }
    private function setId($value)
    {
        $this -> id $value;
    }

    public function getProperty($property)
    {
        return $this -> properties[$property];
    }
    
    public function getItems($table,$fields=false)
    {
        $query 'SELECT '.(is_array($fields) ? implode(',',$fields) : '*').' FROM `'.$table.'`';
        $result metaclass::$db->query($query);
        
        while ($row metaclass::$db->assoc($result))
        {
            $return[] = $row;
        }
        return $return;
        
    }

}
?>

I'm pretty sure this wasn't the latest version, either. (e: definitely wasn't)
e: updated metaclass with a slightly newer version.

Rat Supremacy fucked around with this message at 08:20 on Aug 13, 2009

Adbot
ADBOT LOVES YOU

Begby
Apr 7, 2005

Light saber? Check. Black boots? Check. Codpiece? Check. He's more machine than kid now.

haywire posted:

Cool model class stuff

I think thats good that you are learning all of that, and I went down that same road. However, practically you may find that it might get inflexible down the road and hard to maintain. I posted a bunch of stuff on these and other forums and people told me to not do it, and I did it, and now I am in trouble with a bunch of old poo poo that is really really difficult to maintain and overly complex.

If you do want to do that, its good to learn it, but really you should use something that is community supported, or become a contributor to an existing project.

How I personally do it is this (not saying that you should do it this way, or its the right way, others might say I am crazy). I do this in both PHP and C#.

I have a set of domain classes. These represent the data that my application interacts with. These are by not always 1:1 mappings to a database. Some of the classes get reused within other classes, some are immutable value objects, etc.

For instance, right now I am working with orders and shipments and such. A few of my domain objects are something like

Address
Weight
Country
Shipment
Package
Item
Dimensions

An address contains a country, and a shipment contains two address objects, one for from and the other for destination. Shipment contains a weight object for total weight of all packages, package contains a weight object for total weight of all items, and items contains a weight object. A shipment has a list of packages, and a package has a list of items. The weight object allows me to read out ounces/pounds, or get it as a decimal. Dimensions allows for calculating total volume, converting between cm and in, etc.

The only thing that goes in these domain objects is data and some logic to manipulate and check said data. Having these completely separate from the database allows me to structure them however makes most sense for working with the objects. It also allows me to easily unit test everything.

Then for the above domain objects I have one class that reads and writes shipments to the database (since my application pretty much treats shipments like a single entity and deals with them in their entirety) and has methods like

Shipments.Validate(Shipment shipment)
Shipments.Save(Shipment shipment)
Shipments.Void(Shipment shipment)
Shipments.Find(shipmentID)
Shipments.FindByReference(reference1)
Shipments.Search(SearchParams search)

Search params is a simple class where you can assign things like a date range, vendor, etc. Then the shipments class turns it into a where clause.

All the database stuff is stored within the shipments class. When you save a shipment it writes to something like 8 different normalized tables which differ somewhat from the domain objects. For instance weight is stored as just a decimal, dimensions are stored as three fields for length height and width, and the addresses (even though they are a separate object) are stored in the main shipments table like fromCompany, toCompany, fromName, toName etc.

This approach might seem like it might take longer to write since there is a lot of classes to create and queries to write. However, its very easy to maintain and I think if I tried to do it with some sort of magic class I would run into issues and have to change my domain objects so they worked with the db class or do some hacking.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply