Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


gizmola last won the day on November 17

gizmola had the most liked content!


About gizmola

Contact Methods

  • AIM
  • Website URL

Profile Information

  • Gender
  • Location
    Los Angeles, CA USA

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

gizmola's Achievements

Prolific Member

Prolific Member (5/5)




Community Answers

  1. Well yes, it violates relational database design rules, which are referred to as the rules of normalization or normal forms. This is in violation of the most basic rule, meaning your table can not be in 1st normal form, by doing what you are trying to do. See this article. Your instinct to put multiple values (categories) in a single column/row combination, tells you that you are going down the wrong path. Furthermore, if your table has category1, category2, category3 etc, then that is a repeating group which is also incorrect. There are many issues here: You can't add a new category position without changing the table structure & all associated queries Queries are ugly and inefficient because you will need to index every category column and have a query that has code like "if category1 = 3 or category2 = 3 or category3 =3" etc. What is the right structure? You need to understand the relationship between the entities (thing, category). Start with the relationship from "thing". What have you told us? "A thing can have many categories". So the relationship of thing -> category is "one thing to many categories". Now look at the relationship from category to thing. What do we know? "One category can define many things." So the relationship from category -> thing is "one category to many things". This tells you that the relationship between thing and category is actually Many to Many. Thing about it for a minute, using simple examples. Thing1 (category1, category2, category5) category5 (thing1, thing20, thing1000) Currently you don't have a table for category. You need to make one. category -------- id smallint unsigned primary key AUTO_INCREMENT category varchar(80) This table should be loaded with all your 1-n categories. Create a table to resolve the many to many relationship between thing and category. Typically people name a table like this "thing_category". It only requires 2 values: thing_category -------------- thing_id category_id You can give this table its own unique primary autoincrement key if you want or define the primary key to be thing_id,category_id. It is important either way, that you have a key that guarantees uniqueness for a thing_id,category_id value. Hopefully can now see how you would use this table. If I want to set thing1 to have categories 3,7 and 9, then I only need to insert rows into thing_category of (1, 3), (1, 7), (1,9). To get the categories back out, you join the tables together. Your queries are simple when you need to query for a particular category -- just inner join thing to thing_category and specify WHERE category_id = 7, or whatever you need. If you want a few categories, the query can be WHERE category_id IN (3,7) etc. This also makes your system configurable and data driven, as new categories can be added to the category table at any time, and you can start to make use of them without having to change code, since nothing will be hard wired in.
  2. My suggestion would be to look at the template code, and look at what happens when you navigate to http://mentalhospital.rf.gd/index.php?/recent_pics. Whatever querying is done for that, you want to add some of that logic to the default/homepage section of code so that it selects however many pictures you want in this block. Then you'll need to edit the home page template and add this new block in whatever way you want. Like most packages of this sort, you have to go in and edit the theme file(s) to customize it beyond whatever customization options the package offers, which in this case, it appear are few.
  3. Each method or function should do one thing. You have lots of redundant/competing object instantiation going on. Think about what your classes do/are for. What purpose do you need. You also seem to have a basic misunderstanding of how class properties are available/used. 1st things 1st. One source file per class. Only the namespace statement and class code. The filename should match the classname. Once you get the hang of this things will start to come together quickly. 2 Things you can learn more about to upgrade your code/understanding of PHP Oop. Learn about PHP Namespaces. Start using them. Add composer to your project mix with an initial composer.json. PHP Namespaces: The basics of them PSR-4: How to name your classes and organize them in your source tree to match, so they can be autoloaded Using Composer for package management and autoloader generation Learn about Dependency Injection as a design pattern. This was suggested to you previously. A blog series on DI by Fabien Potencier, creator of the Symfony framework project To do PSR-0/4 compatible namespacing, you need an orgname. It doesn't matter what it is, but case matters. So for example, let's say you were writing things for Phpfreaks, and the team decided all code would use that as the organization name. Then the Top namespace is going to be Phpfreaks. In this example, Somename is a stand in for whatever you want to use for your code. Let's try a new Db class: <?php // Filename /src/Database/Db.php // In composer.json you will have a PSR-4 mapping for Somename mapping to your project src directory. You will place all your classes in directories beneath src, organized as you see fit. namespace Somename\Database\Db; class Db { private $host; private $dbName; private $user; private $pass; private $charset; private $pdo; public function __construct($host, $user, $pass, $dbName, $charset="utf8mb4") { $this->host = $host; $this->user = $user; $this->pass = $pass; $this->dbName = $dbName; $this->charset = $charset; $this->connect(); } protected function connect() { try { $dsn = "mysql:host=".$this->host.";dbName=".$this->dbName.";charset=".$this->charset; $this->pdo = new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); } catch (PDOException $e) { echo 'Error: '.$e->getMessage(); } } public function getConnection() { return $this->pdo; } } Before instantiating your Data class create a new instance of a DB class, passing in all the parameters to the constructor. In this example, charset is optional. The constructor will try to make the pdo connection. <?php use Somename\Database\Db; use Somename\Database\Data; $db = new Db('localhost', 'user', 'pw', 'database'); // Data class should store the $db parameter in a private variable in the constructor. $data = new Data($db); Then from within data you could be using $this->db for pdo connection related things.
  4. It seems like a pretty good design, however, having a Logo page with an image is kind of old school, and not accessible. You should have an alt tag at very least. You want to go over all the accessibility features and make sure you've implemented them properly. The front page of the site spacing could use some margins so your form elements don't bunch together quite as much. The actual results are ok. Having everything centered doesn't look great to me. I would probably suggest either a table or perhaps a ul. It's odd to see a paragraph with all the text center aligned so that the 2nd line with 5 words is in the middle of the first one. Again I'd fine tune the markup and css a bit, and perhaps have some subtle background colors and borders around the various result boxes, to make it look a little bit nicer. The pre tag for your whois information is not responsive. Styling it this way would probably be helpful. pre { white-space: pre-wrap; word-wrap: break-word; text-align: justify; }
  5. Yes, although again the UI is incorrect. Checkboxes are not mutually exclusive. So you are just doing this UI in the wrong way. Your form code is also wrong. You have a label that is hardwired to dark, yet you have variables toggling the value from light to dark. When a form is rendered, it should always be checked, because you are using the checkbox to toggle from light to dark. This means that a value for the checkbox will never be set in the way you using this. A newly rendered form will always have a value (light or dark) and will be checked. Once you uncheck it, you'll submit the form, and the checkbox will not be passed. However, to your question, you have 2 choices: Write a little js that checks the check/uncheck state and sets a hidden element, which you use on submit Just have php check for isset() or use array_key_exists. If ! isset() then it was unchecked. You want to check the request method to make sure the form was actually posted. Not that I'd advise this, but in this case, and again you have to be logical and clear, if the form is submitted, then you are looking to toggle the value. <php if ($_SERVER['REQUEST_METHOD'] == 'POST') { $mode = empty($_COOKIE['mode']) ? 'Light' : $_COOKIE['mode']; //Toggle it $mode = ($mode == 'Light') ? 'Dark' : 'Light'; setCookie('mode', $mode); } else { $mode = empty($_COOKIE['mode']) ? 'Light' : $_COOKIE['mode']; setCookie('mode', $mode); } // Now can use $mode to render ?> <input type="checkbox" onChange="this.form.submit()" value="<?= $mode ?>" name="mode" checked> <label for="mode"><?php echo "$mode Mode"; ?><label><br> Again when you think about it, the actual value of the checkbox is irrelevant as you are really going to toggle the cookie value on any submit. The other option is to use some js. Probably what I would do is use a hidden field, so that you could rely on the submission, but then when you have a cookie you are also relying on, this whole scheme makes less sense Something like this could work: <?php if ($_SERVER['REQUEST_METHOD'] == 'POST') { $mode = empty($_COOKIE['mode']) ? 'Light' : $_COOKIE['mode']; //Toggle it $mode = ($mode == 'Light') ? 'Dark' : 'Light'; setCookie('mode', $mode); } else { $mode = empty($_COOKIE['mode']) ? 'Light' : $_COOKIE['mode']; setCookie('mode', $mode); } ?> <!DOCTYPE html> <html lang="en"> <head> <title>Form Test</title> <script type="text/javascript"> function toggleMode() { let cb = document.getElementById("cbmode"); let newMode = document.getElementById("new_mode"); newMode.value = (cb.value == "Light") ? "Dark" : "Light"; console.log(newMode.value); document.getElementById("modeForm").submit(); } </script> </head> <body> <form id="modeForm" method="post"> <input type="checkbox" onChange="toggleMode()" value="<?= $mode ?>" id="cbmode" name="cbmode" checked> <label for="cbmode"><?php echo "$mode mode"; ?></label> <input id="new_mode" type="hidden" value="<?= $mode ?>"> </form> </body> </html>
  6. The biggest changes in the PHP world: Additions to the PHP language Deprecation of MySQL_ Addition of namespaces and PSR-1/Now PSR-4 Creation of Composer and packagist Ascension of git to standard (Github, Bitbucket, Gitlab) Big 2 MVC frameworks (Symfony/Laravel) Vast improvement and adoption of Component libraries, installed via composer Explosion of cloud computing/hosting The biggest changes to methodology: Move to using Docker & Docker-compose for development Use of Git For editors, most pro developers are using phpStorm. It's the best PHP IDE, and the one you most commonly see being used. Visual Studio Code is pretty competent, and is free. There are a few PHP extensions that make it quite competitive to phpStorm, but you have to do some work in getting it setup. Both editors have great support for docker and git, although I tend to be a cli person, and feel more comfortable having a terminal open and multiple displays. They both have integrated terminals and you can basically stay in the IDE window 99% of the time, when iterating. As a long time user/early adopter of virtualization (Xen, Virtuozzo, VMware, Parallels, Virtualbox, Vagrant) I have used nearly all the virtualization and development orchestration options at one time or another, but at this point, Containers have proven to be superior for development, when compared to full OS virtualization, and have become the primary platform for deployment at scale. Given your stack requirements, Docker is a great solution, because you can easily iterate through the various versions and combinations of the stack under Docker, without having to build full virtualized OS's or install any packages. Just use the Official Docker images for PHP and MySQL. The PHP container already has versions of apache, simplifying your setup. Git is a great facilitator for an upward migration process, as you can make a new branch each time you go up a PHP version, fixing things you need to fix as you go, and allowing you to switch back and forth between branches or have multiple git clones where you could conceivably be running an older version of the app against an older stack, while simultaneously running a newer version that is in the development process. For getting bootstrapped into Docker, there are projects like Devilbox, Laradock, or even my relatively new project docker4lamp. These can all provide a basis for you to get comfortable with Docker and how to orchestrate containers together. I'd promote Docker4lamp more in terms of its stack matching your target, but the purpose of the project is to have a latest/greatest stack for someone relatively new, but with a solution to all the various problems that new developers confront at some point. Being far simpler in scope than either devilbox or laradock, it might be easier to learn from, as you may want to have your own docker images and docker-compose added to your source. Needless to say there is an incredible amount of information you can find to help you jumpstart your understanding and use of Docker. Of the 2 "kitchen sink" packages I mentioned, probably laradock (which despite the name, will give you a complete set of containers to work with) is going to be easier to get up and running with. Quality documentation exists for both projects. Once you have done all the porting work you need to do, you can decide if you want to deploy onto an OS with packages as you have been in the past, or use Docker in production under Kubernetes or docker-compose on linux. I also have been a longtime Centos user, and recently setup a hosted vps under Alma. The main thing I learned is that you want to use dnf and not yum, but otherwise it looks like Centos. I will say that for a long time, the Centos official packages had to be worked around, and people were using epel and remi to get anything close to current. It's really a pain, and yet another reason to consider just running containers instead. For a reverse proxy, one relatively recent option that has emerged is traefik. It's a really nice option, and far less complex to get going than nginx or squid or ha. With that said, usually scalability involves substantial architectural decisions and the avoidance of building in things that require a monolithic infrastructure. Step 1 would be getting your app current to a supported version of PHP. You might also want to upgrade your MySQL version to something fresher. Step 2. would be looking at performance/scalability, if there are any significant issues. Full disclosure, most of the projects I've worked on in the last 10 years have been deployed within AWS.
  7. An unchecked checkbox isn't passed when a form is posted. You could correct for that, or you could use a more appropriate form element (radio button or select list).
  8. Just looking at your design, the obvious missing piece is that you don't know when a particular award-skill is passed, so that's a benefit to the system, although maybe that isn't something you can manifest at this juncture. Looks like your new tables would be: StudentAwardSkill ----------------- person_id award_id display_ord date_skill (optional) You need a unique index on (person_id, award_id, display_ord) The scary thing about the current design, is that if anyone were to modify the display_ord value of any skills, it would basically change/corrupt the entire meaning of any stored Skills in the StudentAwardTbl.skills column. You might be wondering how you can build the bridge I described: Your new app should only be concerned with writing out the proper relational design - writing rows to StudentAwardSkill. Your checks = UPSERT (typically use INSERT ignore.) You attempt to insert a new row for every checkbox using person_id, award_id, display_ord. Set date_skill to now() if you want to use that. The main issue with this design is the handling of "unchecked boxes" . An unchecked box means that you need to issue a delete against StudentAwardSkill, again for that person_id, award_id, display_ord combination. You will need a table named something like AwardSkillSync AwardSkillSync --------------- awardskillsync_id created_on (timestamp) changeby (tinyint) student_id award_id processed (tinyint, default 0) When your endpoint runs, you create a row in AwardSkillSync, with changeby = 1. StudentUpdateTrigger runs when StudentAwardTbl.skills is changed. Creates an AwardSkillSync row with changeby = 2 row. Now you are only left with writing a sync command line script. That script should query for oldest rows with processed = 0. It should SELECT for update, and depending on the changeby it will either look at the flags in StudentAwardSkill and conform the values in StudentAwardSkill to what is in that array OR query the values from StudentAwardSkill, and generate the string, updating it. If there are few users and few updates, most likely changes will be synchronized in either direction in what appears as near instantaneous fashion. There will be extra queries needed when a change is made in the old delphi system, but that is the cost to being able to have this bridge with no changes to the existing delphi system. The important thing to keep in mind for this to work, is that your sync routine should not update StudentAwardTbl.skllls if things are already correct. Just mark the row as processed. Although a bit less important, as no trigger is involved, would be any updates to StudentAwardSkill. Once the sync worked for either update, you could put it in a cron that you could run every second. You want to make sure that you use some type of semaphore that would prevent the job from running if another job was already in process.
  9. Systems are often hamstrung from the get-go with bad database design decisions. Since you are doing this for a charity (and I assume, donating your time?), I really don't see any reason to force yourself to build upon a poorly designed system. If you're doing this in part to hone your skills, then there's even less reason to reinforce a bad design, by building something new against it. There is a way to continue to use both systems, which is to create the tables you should have, then develop some bridge code that will keep them in sync. It's not simple, but it can be done, and then you won't be stuck with the mistakes of the past forever. As for updating in page, the answer is to use ajax. Have the button click submit values to an endpoint you develop that take the changes and modify the data as needed. Modern ajax code should be written using ES6 and best practice techniques: Use Fetch with promises (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) Pass data to/from your ajax endpoint in json format I don't know what version of bootstrap you are using, but resist the temptation to utilize jquery for this stuff, even though jquery does have the $.ajax() call, which could be used. As for your PHP endpoint, just think of it as the same script you might write if you had a standalone form that was submitting only the data you need to do this update. You will want to read the data from POST, json_decode it, and from there the actual processing should be straightforward.
  10. As an aside, Light/Dark themes are a great use case for SASS/SCSS. Here's a short article to illustrate some of the ideas.
  11. Hey I didn't name the field 😅 But my american upbringing says that "specialty" is good. I do like the english pronunciation of spe-cee-al-it-tee of your version though.
  12. My First comment: Don't use enums. I would recommend changing the definition of the field to a CHAR[1], and store either a 'P' or an 'S'. Your code should not interpolate values into your SQL even if you are escaping them. Use Prepared Statements. Your redirection code should be changed: if ($specialty == "pro") { header("Location: pro.php"); } elseif ($specialty == "stu") { header("Location: stu.php"); } else { die('Unknown Specialty.') } Note format of the Location header.
  13. I already debugged this for you. Did you use the code I posted? It was tested, and doesn't have any bugs involving the $ques variable being undefined.
  14. It was something I used to test out a docker project I've been working on, so it really didn't take long. Gave me an excuse to play with it.
  15. I used your database structure and values. You have an issue with the data in quiz_answer. I am going to leave it to you to figure out what is wrong (Hint: the answers need to match the correct option_id!) Here are the files I used. You will need to have valid database host, name, password and db variables defined in the quiz class, so that you are connecting to the database. One thing you need to understand is that when you submit a form ALL the fields in the form are submitted, including buttons. Everything will be in the $_POST array. I added a line of code to deal with this problem. I also added some code to display the grading step for each question, so you can better understand what is happening. Some good functions you can use to help you with simple debugging: When you don't understand what a variable contains try adding print_r($variable);die(); This will show you the value of the variable and stop processing. You don't have to stop processing, but having a lot of these statements is often confusing. Solve one issue and move on to the next. The biggest issue with this code is the use of numeric indexing of your mysql data with mysqli_fetch_all. Things would be better if your mysqli fetch routines used mysqli_fetch_all($result, MYSQLI_ASSOC); Working with array keys is much clearer, not to mention safer than having code like: (if $somevar == $array[0][2]). That code would be something like $somevar == $array[0]['option_number'] instead. //quizclass.php <?php class Quiz { // Database credentials private $host = 'hostname'; private $username = 'username'; private $password = 'password'; private $database = 'dbname'; public $db; public function __construct() { if (!isset($this->db)) { // Connect to the database try { $this->db = new mysqli($this->host, $this->username, $this->password, $this->database); } catch (Exception $e) { $error = $e->getMessage(); echo $error; } } } public function get_questions() { $select = "SELECT * FROM `questions` where is_enabled = '1' "; $result = mysqli_query($this->db, $select); return mysqli_fetch_all($result); } public function quiz_options($qid) { $select = "SELECT * FROM `quiz_options` where qid = '$qid' AND is_enabled = '1' "; $result = mysqli_query($this->db, $select); return mysqli_fetch_all($result); } public function answer($qid) { $select = "SELECT * FROM `quiz_answer` where qid = '$qid' "; $result = mysqli_query($this->db, $select); return mysqli_fetch_all($result); } } //index.php <html> <head> <title>PHP Multiple Choice Questions and Answers</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> </head> <body> <?php include 'quizclass.php'; $db = new Quiz(); $quesions = $db->get_questions(); ?> <div class="container"> <h1>Multiple Choice Questions Answers</h1> <p>Please fill the details and answers the all questions-</p> <form action="score.php" method="post"> <?php foreach($quesions as $ques) { $options = $db->quiz_options($ques[0]); ?> <div class="form-group"> <h4><?php echo $ques[1]; ?></h4> <div class="input-group-text" style="text-align: left; font-size: 18px;"> <ol> <?php foreach($options as $option) { echo "<li><input type='radio' name='".$option[1]."' value='".$option[0]."'> ".$option[2]."</li>"; }?> </ol> </div> <?php } //end foreach ?> <div class="form-group text-center"> <input type="submit" value="Submit" name="submit" class="btn btn-primary"/> </div> </form> </div> </body> </html> //score.php <?php include 'quizclass.php'; $db = new Quiz(); $score = 0; foreach ($_POST as $k => $v) { if (is_int($k)) { $answer = $db->answer($k); if ($answer[0][2] == $v) { // option is correct echo "Question {$answer[0][1]} was correct.<br>"; $score++; } else { echo "Question {$answer[0][1]} was wrong.<br>"; } } } $score = $score / 4 *100; if($score < 50) { echo '<h2>You need to score at least 50% to pass the exam.</h2>'; } else { echo '<h2>You have passed the exam and scored '.$score.'%.</h2>'; }
  • 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.