Jump to content

requinix

Administrators
  • Posts

    15,229
  • Joined

  • Last visited

  • Days Won

    427

Everything posted by requinix

  1. For arbitrary questions and answers of various types (which is more than I initially described), questions id | type | question ---+------+-------------------------------------------------- 1 | text | Describe yourself. /* whoami */ 2 | enum | What kind of relationship are you looking for ? /* looking_for */ 3 | enum | What is a polite, PC way of asking for ethnicity? /* ethnicity */ question_enums id | questionID | answer ---+------------+----------- 1 | 2 | Short-term 2 | 2 | Long-term 3 | 2 | Marriage answers id | userID | questionID | answer_text | answer_enum ---+--------+------------+-------------+------------ 1 | 1 | 1 | text here | NULL /* user #1 describes themself as "text here" */ 2 | 1 | 2 | NULL | 3 /* user #1 wants marriage */ 3 | 1 | 3 | NULL | 15 /* user #1 is east indian */- All questions are either free-form text or multiple choice (that column would be an appropriate place to use ENUM)- Arbitrary list of questions - Arbitrary list of enum values Query to get just someone's answers can go like SELECT q.question, q.type, a.answer_text, qe.answer AS answer_enum FROM questions q LEFT JOIN answers a ON q.id = a.questionID LEFT JOIN question_enums qe ON q.id = qe.questionID AND q.type = "enum" AND a.answer_enum = eq.id WHERE a.userID = $userID ORDER BY q.idNote the outer joins:- LEFT JOIN answers because the user may not have answered a question yet - LEFT JOIN question_enums because a question may not be an enum type If you make sure that enum answers do not contain a particular character, like the pipe |, then it's easy to get all answers with the questions too, such as when the user is answering the questions: SELECT q.question, q.type, a.answer_text, a.answer_enum, GROUP_CONCAT(CONCAT(qe.id, ",", qe.answer) ORDER BY qe.id SEPARATOR "|") AS enum_answers FROM questions q LEFT JOIN answers a ON q.id = a.questionID LEFT JOIN question_enums qe ON q.id = qe.questionID AND q.type = "enum" WHERE a.userID = $userID GROUP BY q.id ORDER BY q.idenum_answers would look like "1,Short-term|2,Long-term|3,Marriage", and you can use a bit of PHP code to break that back apart into the individual answers with their IDs. Obvious places for improvement: - Sorting questions, like by a simple integer field that you then ORDER BY - Likewise, sorting enum answers because sometimes there's a obvious ordering that should be used (eg, level of education) - Partitioning questions into "groups" or "pages" - Text questions should have length limits, like 100 for short answers and 1000 for long answers, that should vary by question - Allowing multiple answers for an enum (as in "pick all that apply")
  2. What happens when you want to add a new religion? Or hair color? Use lookup tables. Each has an ID number and a value, like 1/Auburn and 5/Regularly. You could actually combine the tables into one by adding a column indicating what the value is (eg, 1/hair color/Auburn). But let's keep going. What happens when you want to add a new question? Or remove one? Use another table. It works best with the combined-tables version above: one table with an ID and question, then the second table with an ID, question ID, and answer. The answers themselves then move out of the user into and into a third table containing the user ID and answer ID.
  3. A reasonable statement, but subject to interpretation. My interpretation is not "don't use exceptions at all" but rather "don't propagate normal-use exceptions". For your code there it is not necessarily "exceptional", however the factory doesn't know that. And it's the one throwing the exception. All it knows is that it was told to create something that it can't do. As such it should throw an exception because in the general case it should only be called when it is able to do its job. What you're doing is using that as a form of validation: instead of writing your code such that it only tries to create labels for things that can have labels, you're pretending that everything can and then waiting for something to tell you that you're wrong. Catching all exceptions is a bad practice, and violates the "don't propagate normal-use exceptions" interpretation*, so try { $x->getLabel($productLine); } catch (NoLabelForProductLineException $e) { // ignore }Or to bypass the exception issue entirely, foreach ... { if (!$x->hasLabel($productLine)) { continue; } $x->getLabel($productLine); }* Well, the implied "...but do propagate unexpected exceptions" part of it.
  4. They solved it? Tell them it's happening again but this time ask what they did to fix it.
  5. You never need to use exceptions. There's always an alternative. The question is whether exceptions are a better choice. First, exceptions versus logging. I see two potential problems that need to be accounted for: 1. The factory cannot produce the type of thing that was requested of it. If you log a message here then it should be about how the factory could not do its job. 2. That code you've posted can't get a label and thus cannot do whatever it was trying to do. If you log a message here then it should be about how the code could not do its job. Personally I would probably do both. The first one is urgent as it means somebody didn't write some code for the new product and that needs to be fixed as soon as possible. The second one may or may not be urgent, but at the very least a failure probably means that somebody needs to take some sort of action. It's possible for the factory to fail when used elsewhere, and it's possible for this code to fail while the factory succeeds, so neither is dependent upon the other. As for whether to use exceptions, that's a question without a definite answer. PHP traditionally doesn't use exceptions much (though that will start changing drastically with PHP 7!) and errors are propagated by using null and false. Your factory could do the same: return null for failure, and make sure code calling it (and potentially code calling that, and so on) is aware that the factory may return null. So no writing Factory::createTheThing($data)->method();unless you are okay with your code potentially crashing. Which you shouldn't be. Exceptions are a bit nicer in that your code can, for the most part, assume success in cases where it doesn't need to handle error. For example, getLabel() may not need to care if the factory fails, however something somewhere should care (such as your code there) or else the exception will be handled by PHP as a fatal error. It's also much, much easier to pass along different types of failure. If the factory returns null then you probably don't know why and it could have been one of many types of errors, however you can differentiate a ProductClassNotFoundException from a InvalidProductClassException. Personally I use both but it's hard to explain the reasoning I use for both. (One of those "it just makes more sense this way" things.)
  6. Run the WinSAT test (Control Panel > System > one of the links on the page), wait for it to finish, go into the detailed results, and post them here. So what are you trying to do that's so slow? Have much running at once? Lots of installed applications? Are you aware that a dual-core Pentium, 2GB RAM, and integrated graphics will not be very powerful?
  7. No, they're two different things. First came mysql and later came mysqli ("mysql improved") so they're related, but they're still different.
  8. You'll get that message regardless of whether the query works. You need to check the return value of mysql_query(): it will return true for success and false for failure. But that only checks whether the query successfully ran - not whether the query inserted any records. Use mysql_affected_rows for that. You're starting off, right? Don't use the mysql extension and mysql_* functions. They're old and slow and not as good as the alternatives: PDO or mysqli. Look into both and decide which you'd rather use (the overall style is similar but there are a few differences here and there).
  9. Not with set_error_handler() per se, but debug_backtrace will work. It may include the call to the error handler too (probably) so you'll want to trim that off.
  10. Do most programmers use OOP to generate HTML? Not often.Do most programmers who need something to help them generate HTML use OOP? Probably many do. HTML generation code tends to be a) A function. Something that generates an HTML table isn't really an object per se. b) A sort of "HTML table factory" class, where it makes sense to go OOP as the class represents a thing that creates tables. And that's not just semantics. c) A utility class geared towards generating HTML, and one (or more) methods would be about generating tables. Not OOP in the truest sense but merely a bunch of functions all put in one place. Like I showed above, it could be implemented reasonably using OOP, or implemented reasonably not using OOP. Personally I've always wanted a lot of control over the table markup and creating a generator to accommodate everything is a hassle that's not worth the time. That argument does hold water, but it needs to be weighed against other arguments. For example,- Is it maintainable? If there is a bug with the implementation, how easy would it be to fix? - Is it extensible? A big part of OOP is about reusing classes - mostly through inheritance. Can this class be extended to create another table generator that is somehow "more specific"? (If such a thing could possibly make sense.) - Is the code acceptable to someone who is familiar with object-oriented programming? It may not be ideal but is it at least something the person won't scratch their head about wondering what could have possessed someone to write that code. So a generic class for database connections, and a child class that adds MySQL queries? Okay. Less okay. Extending a class should be about specializing it. Extending a class because you want to use its functionality is not very good; more often you should simply have an instance of the first class as a member variable in the second class. class DatabaseProvider { ... } class DatabaseConsumer { private $database = null; public function __construct() { $this->database = new DatabaseProvider(); } }In my mind there are four classes so far: // generic database stuff // abstract because this class doesn't know enough information to actually communicate with a database abstract class Database { ... } // specialized database stuff class MySqlDatabase extends Database { ... } // potential related classes class PostgreSqlDatabase extends Database { ... } class OracleDatabase extends Database { ... } class MongoDatabase extends Database { ... } // generic html generator, probably has some common functionality and helper methods // abstract because you need the specialized knowledge in subclasses in order to generate particular html elements abstract class HtmlGenerator { ... } // specialized <table> html generator class TableHtmlGenerator extends HtmlGenerator { ... } // potential related classes class ListHtmlGenerator extends HtmlGenerator { ... } class ImageHtmlGenerator extends HtmlGenerator { ... } class LinkHtmlGenerator extends HtmlGenerator { ... } The first two help you connect to the database while the second two help you generate your table markup. I don't actually see a need for another class that (presumably) uses the database to get information and then pass it along to the generator class for rendering. That's actually the kind of thing that I would leave procedural: what you're doing is heavily dependent upon what this table is for and where you're putting it. $database = new MySqlDatabase(); $users = $database->getRecentSignedUpUsers(10); // ten most recent // so very many ways you could do the table generation. here's one idea: $table = new TableHtmlGenerator($users); $table->addColumn("User ID", "id"); // heading, array key $table->addColumn("Username", "username"); $table->addColumn("Registration Date", "registered"); $table->output(); I touched on this subject with the HtmlGenerator, TableHtmlGenerator, and example code. In general a class should represent a blueprint to an object; in this case the TableHtmlGenerator would contain a blueprint to an HTML table. (More precisely it's a blueprint for a thing that can generate an HTML table.) That class also has a method where it can take the blueprint and produce an actual thing (the output). Taking a class for generating a table, then extending it in order to provide details doesn't make so much sense. What good is that first class if it doesn't have details? It won't be able to do anything. It could make sense to extend that first class with another subclass that provides different details, but that could turn into a slippery slope where you need tons of subclasses for the tons of different details that could be shown in a table. If you think about it, tables all use basically the same details: headings and data, (naturally it can get more complicated) so you could just have that first class accept details from somewhere? Now you have the TableHtmlGenerator. Thinking about it some more, you could decide you want another class: one for generating lists. You go about making it and then realize that you're writing a bunch of code that looks similar to what's in the TableHtmlGenerator: start tags, end tags, attributes... Individual HTML elements all work the same way and what varies is what elements you use in what order. That's where the HtmlGenerator class comes in. It's not very useful by itself as the idea of blueprint for a universal HTML generator is a bit too broad to cover in one class, but it can include logic that other HTML generator classes would find handy. abstract class HtmlGenerator { protected function writeElement($tagname, array $attributes = array(), $innerHTML = null) { echo "<", $tagname; foreach ($attributes as $attr => $value) { echo " ", $attr, '="', htmlspecialchars($value, ENT_QUOTES, "UTF-8"), '"'; } echo ">"; if ($innerHTML !== null) { echo $innerHTML, "</{$tagname}>"; } } protected function writeEndElement($tagname) { echo "</{$tagname}>"; } } class TableHtmlGenerator extends HtmlGenerator { public function output() { $this->writeElement("table"); if ($this->columns) { $this->writeElement("tr"); foreach ($this->columns as $heading) { $this->writeElement("td", array(), $heading); } $this->writeEndElement("tr"); } // ... } }
  11. Okay, so there may not be a record in LocalidadEstado (available) or there may be with IdEstado=1 (available) or 2 (not available)? I'm guessing no records means no purchase, 2 means a purchase, 1 means a purchase that was cancelled or similar so the seat is available again. Try a LEFT JOIN SELECT ... FROM TeatroAsiento U JOIN PrecioEspectaculoSector P ON U.Sector = P.Sector LEFT JOIN LocalidadEstado L ON U.Id = L.IdTeatroAsiento AND P.IdFuncion = L.IdFuncion WHERE U.Sector = '$Sector' AND P.IdFuncion = '$fn' AND P.IdEspectaculo = '$id'This way you'll get a row from TeatroAsiento+PrecioEspectaculoSector regardless of whether there's a matching LocalidadEstado record, however any LocalidadEstado.* columns will be NULL if there wasn't a match. So - L.IdEstado IS NULL: Available - L.IdEstado=1: Available - L.IdEstado=2: Not available and if ($LocalidadEstado == 2) { // not available } else { // ==1 or ===null // available }
  12. Have you checked your error logs yet?
  13. Can you post some sample data from those three tables? I'm still not quite getting how this is supposed to work.
  14. Not from me. It's pretty straightforward though, most tutorials will have about the same thing: receive big $_POST array, validate with PayPal by sending them almost the same thing, then do what you want with the data. I suggest storing all the IPN data in a table somewhere, though. It's great to have it available if you need it and will include things like dates, email addresses, money amounts, transaction and billing IDs...
  15. No, none of that is IPN. The $payment and/or $execute objects may have the data you need somewhere in them. Or methods to get that data. Look in there.
  16. So you want to show the various seats in a particular sector for a particular show? The relationships that I see are 1. TeatroAsiento.Id = LocalidadEstado.IdTeatroAsiento 2. TeatroAsiento.Sector = PrecioEspectaculoSector.Sector 3. PrecioEspectaculoSector.IdFuncion = LocalidadEstado.IdFuncion Which table you select FROM and which you JOIN doesn't matter very much, so SELECT ... FROM TeatroAsiento U JOIN PrecioEspectaculoSector P ON U.Sector = P.Sector JOIN LocalidadEstado L ON U.Id = L.IdTeatroAsiento AND P.IdFuncion = L.IdFuncion WHERE U.Sector = '$Sector' AND P.IdFuncion = '$fn' AND P.IdEspectaculo = '$id'Looking at your query again, I see that it does kinda have relationships. It's not clear at first sight but because you use $Sector and $fn in multiple places, you basically end up with a relationship.Anyway, your query gets very close to the above but it doesn't cover the first relationship: TeatroAsiento.Id = LocalidadEstado.IdTeatroAsiento. You can add it into your query, or use the JOIN form (which I recommend) like above.
  17. I don't follow your description, but looking at the query it doesn't seem right: you're selecting from three tables at once and there aren't any relationships set up between them. That means you'll get a cross-product of all the rows: every matching row in TeatroAsiento coupled with every matching row in PrecioEspectaculoSector coupled with every matching row from LocalidadEstado. My Spanish is horrible. How do those three tables relate to each other? What columns in each table share values with what columns from another table?
  18. That code doesn't look like IPN... IPN is where PayPal hits a URL on your server with information about some thing they did (eg, transaction). Do you have something for that?
  19. It does... what, exactly?
  20. It's not a counting "method" but a different situation. I'm going to stick with the racing analogy. You're picking one driver and one car at a time (A1). The other drivers and the other cars are irrelevant. This driver+car does a qualifying lap around the track. You then give them a different car (A2), they lap, then lap again with the third car (A3). Repeat that with the other two drivers (B1-3, C1-3) and you have a total of nine qualifying laps. This way you can see a driver's performance change with respect to which car they were driving (A1-3, B1-3, C1-3), or alternatively how different drivers handle the same car (A-C1, A-C2, A-C3). grissom is picking all the drivers and all the cars and putting them into a race all at once. Then the assignments change, except rather than have every driver get a different car, what changes is that somebody (perhaps more than one person) gets a different car. Thus the race lineup as a whole changes, even if some or most (but not all) of the drivers stay in the same car they were just driving. The combinations are 1. A1B2C3 2. A1B3C2 3. A2B1C3 4. A2B3C1 5. A3B1C2 6. A3B2C1 This is more geared towards measuring the performance of a driver with respect to how the other drivers are doing. As is what happens a real race. For example, with races 1 and 2 you can see how driver A (who kept the same car for both) did when B and C changed cars. If you think A's car is the variable, you can compare races 3 and 4 where A used a different car from before (but kept it for both races) and B and C changed. Or races 5 and 6. For driver B you can compare 3+5/1+6/2+4 and for C it's 4+6/2+5/1+3. tldr: you're counting driver+car combinations, grissom is counting driver+car+driver+car+driver+car combinations.
  21. The data is the entire response, not just a part of it. The items array is at .items and that's what you should be looping over. Inside the function, v will be one of the items and you should use .id.videoId on that.
  22. Follow along with the various {s and }s, see where the various functions and control structures (if, foreach, etc.) start and end, and eventually you'll realize you're "inside" the class and "outside" everything else and missing the final closing }. Looking at the indentation works too, as long as the code is properly indented (which that code is).
  23. Why it says a semicolon, I don't know, but you are missing the closing } on the class.
  24. What do you have so far? Also, birthdate BETWEEN CURDATE() - INTERVAL agefrom YEAR AND CURDATE() - INTERVAL ageto YEARis incorrect. 1. The dates are in the wrong order: since agefrom and ", which won't work. 2. Off by one error. Consider if agefrom=ageto: the two dates will always be the same and the query would only return people born on exactly that date. There needs to be a gap of a year, and that gap goes on the older side. Together, $agetogap = $ageto + 1; "birthdate BETWEEN CURDATE() - INTERVAL {$agetogap} YEAR AND CURDATE() - INTERVAL {$agefrom} YEAR"
  25. The socket is in /tmp? Odd place. It's normally under /var/lib/mysql. Is the client side definitely set up to look there for the socket file?
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.