Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by ignace

  1. It doesn't know that's the entire point. someCodeThatCanDumpThings(VarDumper $dumper) The only thing that this function/method knows is that it can call $dumper with a method dump() which has certain arguments. The implementation details however it is unaware of. If this is to HTML or CLI or SOAP. It doesn't actually care. Through dependency injection you'll define what the implementation is going to be for the VarDumper interface.
  2. You can also pass the IP address as a constructor argument. Decoupling your class from the globals. If you use dependency injection it's even easier to do. Mind that when you are behind a proxy getting the IP address is not as simple as querying REMOTE_ADDR which further warrants the need for injection. class LoginAttemptsLog { /** * @var PDO the connection to the underlying database */ protected $database; /** * @var string */ protected $ipAddress; /** * @param PDO $database the connection to the underlying database * @param string $ipAddress the IP of the user */ public function __construct(PDO $database, $ipAddress) { $this->database = $database; $this->ipAddress = $ipAddress; } // .. }
  3. Kinda, it would be something like: class Quote { private $id; // .. other fields private $price; public function setId($id) { $this->id = $id; } public function getId() { return $this->id; } // get*, set* for the other fields public function setPrice($price) { $this->price = $price; } public function getPrice() { return $this->price; } }Then you would create a query and populate this object: $sql = 'SELECT id, .., price FROM quotes'; $stmt = $this->db->prepare($sql); foreach ($stmt->fetchAll() as $row) { $quotes[] = $quote = new Quote; $quote->setId($row['id']); $quote->setPrice($row['price']); } return $quotes;At this point you can work with this object as: $quote->getId(); $quote->getPrice();Now before you start writing all of this yourself, there are frameworks available online that do this sort of thing for you. A list can be found here:https://github.com/ziadoz/awesome-php#database
  4. What you should do though, is set up an auto-response for the noreply address. So that anyone who does decide to mail that address is informed of their mistake.
  5. ignace


    I do not encourage building a full production site from my example code merely use it to understand MVC. Once you do, it is recommended to use a framework to learn the finer points of programming and modern programming techniques since MVC is only a small part of building a web application. Other terms you will cross are Object-Oriented Programming, Dependency Injection, Object-Relational Mapping, Design Patterns (Factory, Repository, ..), .. To come back at your example: class Model { private $pdo; function __construct() { $this->pdo = Database::getInstance(); } function fetchImages() { $st = $this->pdo->prepare("SELECT * FROM GALLERY"); $st->execute(); return $st->fetchAll(); } } You will NEVER have a class called Model. Programming is all about assigning responsibility and proper naming. Naming is actually the hardest part of the job. So the responsibility of your "Model" is to: - return a list of gallery images - add / delete a gallery image So let's settle for now on GalleryManager: interface GalleryManager { public function addImage(GalleryImage $image); public function removeImage(GalleryImage $image); public function getAll(); } At some point we may want to use something different than PDO (Doctrine, Eloquent, ..) to query for our images so we define our manager as an interface. Now for our PDO implementation: class PdoGalleryManager implements GalleryManager { private $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function getAll() { // your code here } public function addImage(GalleryImage $image) { // your code here } public function removeImage(GalleryImage $image) { // your code here } } We also pass PDO through the constructor and not use a Singleton to fetch it to make it easier to test.
  6. ignace


    Look, in short, this is how it works: MVC is a concept divided in three keywords, but each keyword covers a LOT in code. Your Model does all the work. Now the Model is not ONE thing, it's everything that is associated with your business logic. Queries, DAO's, Services, Repositories, Factories, Entities, ... Your View only handles presentation. The View is ALSO not ONE thing, it's everything that handles presentation. By presentation I mean in the case of a web application is HTML. Your Controller has actually no real task, but is required to handle a request, since neither the Model nor View are able (nor should they) to do this. Your header and footer are part of the View and this should be handled in the View. A popular approach is the '2-step View' to this problem: http://martinfowler.com/eaaCatalog/twoStepView.html You can of course use a simpler method if needed, for example: views/gallery/index.php <?php require APP_VIEW_DIR . '/header.php'; ?> <!-- Your content here --> <?php require APP_VIEW_DIR . 'footer.php'; ?> When people first encounter MVC they imagine something very complicated while it's actually really simple: models/gallery/getAll.php <?php $db = require APP_MODEL_DIR . '/db.php'; $stmt = $db->prepare('SELECT * FROM gallery'); return $stmt->fetchAll(); controllers/gallery/index.php <?php $images = require APP_MODEL_DIR . '/gallery/getAll.php'; require APP_VIEW_DIR . '/gallery/index.php'; To make this work you need a "Front Controller", what this means is a Controller that is called first. In Java this is the project class with the main() method. For web applications this is whatever file you tell nginx/apache to load as default, usually index.php <?php define('APP_CONTROLLER_DIR', __DIR__ . '/controllers'); define('APP_MODEL_DIR', __DIR__ . '/models'); define('APP_VIEW_DIR', __DIR__ . '/views'); define('APP_CONFIG_DIR', __DIR__ . '/config'); require APP_MODEL_DIR . '/functions/request.php'; $page = get('page', 'home'); switch ($page) { case 'gallery': require APP_CONTROLLER_DIR . '/gallery/index.php'; break; default: case 'home': require APP_CONTROLLER_DIR . '/home/index.php'; break; } Try to wrap your head around this. You'll see my model is only concerned with model stuff, the view only with view stuff, and the controller just puts the pieces together. This is also true in every framework though it may seem a lot more complicated there.
  7. ignace


    It's kinda correct. Your main controller actually should call the gallery controller, not some view. Inside the controller you would load the view. Something like: <?php $page = !empty($_GET['page']) ? $_GET['page'] : null; switch ($page) { case 'gallery': $controller = new GalleryController(); break; default: $controller = new HomeController(); break; } // do something with $controller
  8. @kicken I tried Laravel recently 5.3 and the same things that bugged you, still bug people. PHPStorm does not understand Laravel with all their facades. They claim they love beautiful code but then go on to do something like: https://laravel.com/docs/5.3/session#registering-the-driver Create an entire new class to simply call a static method on another? Is this something we want to promote? Their expressive interfaces are nice though. And it comes with a lot of functionality out of the box. Where you would need quite a few bundles in Symfony to have the same functionality.
  9. I assume the TFTP server is the CentOS6 server on which you will also host the PHP code? You can write files with: file_put_contents You can read the file with: file use explode to separate Button6 and BLF, and Button6value and 103. At this point you have the data from the file in an array and you can generate an interface for the user to change the function of each button.
  10. No, your response classes should not throw exceptions. My previous post was the answer to why you would want to use multiple response classes for your error responses, nothing more. The doHandle401() etc.. was an example to show the difference between bad designed code and good code using exceptions as an example.
  11. Why would you want to have custom exception classes while you can perfectly use Exception everywhere? try { somethingHere(); } catch (Exception $e) { switch ($e->getCode()) { case 400: doHandle400(); break; case 401: doHandle401(); break; case 405: doHandle405(); break; } } // versus try { somethingHere(); } catch (GenericErrorException $e) { doHandle400(); } catch (UnsupportedMethodErrorException $e) { doHandle405(); } // .. It all boils down to good design. Sure you can do this: return $response->withJson(\MyApp\ErrorResponse::missingKey(), 401);But it does not reflect what you want to do, from a design point-of-view. It may also lead to inconsistencies using 401 here and 400 there. If at some point you want to add custom headers to your errors, you will have to hunt down all uses of ErrorResponse and edit the $response accordingly. Using a class and inheritance avoids duplication and a single point to be edited in case your requirements change.
  12. I have never used Slim before, I have however dabbled with StackPHP. But from reading the documentation I was able to concoct the above example. Regarding ErrorResponse::missingKey() and new MissingKeyErrorResponse() I was of course referring to a parent object containing the error structure which MissingKeyErrorResponse expands upon. class MissingKeyErrorResponse extends AbstractErrorResponse { public function __construct() { parent::__construct(3, 'Missing Key.'); } }Then for the rest of your code, which can be drastically improved: $container['accounts'] = function ($c) { return new \MyApp\Accounts($c['db']); // <-- instead of passing it to EVERY method }; namespace MyApp; class Accounts { private $db; public function __construct($db) { $this->db = $db; } // <-- construction injection FTW public function findOneByKey($key) // <-- proper naming { $stmt=$db->prepare('SELECT id,name FROM accounts WHERE main_key=?'); $stmt->execute([$key]); return $stmt->fetch(\PDO::FETCH_OBJ); } } $app->add(function(Request $request, Response $response, $next) use ($container) { $key = $request->getHeaderLine('X-MyApp-Key'); if (empty($key)) { return new MissingKeyErrorResponse(); // <-- proper response generated } $account = $this->accounts->findOneByKey($key); if (!$account) { return new InvalidKeyErrorResponse(); } $container['account'] = function() use ($account) { return $account; } return $next($request, $response); });If you have a real crush on static methods try this: class ErrorResponseRegistry { public static function missingKey() { return new MissingKeyErrorResponse(); } public static function invalidKey() { return new InvalidKeyErrorResponse(); } } $app->add(function(Request $request, Response $response, $next) use ($container) { $key = $request->getHeaderLine('X-MyApp-Key'); if (empty($key)) { return ErrorResponseRegistry::missingKey(); // <-- proper response generated } $account = $this->accounts->findOneByKey($key); if (!$account) { return ErrorResponseRegistry::invalidKey(); } $container['account'] = function() use ($account) { return $account; } return $next($request, $response); });
  13. I think the main thing you need is starting to learn/read the manual. Why use a framework if you are writing plain PHP anyway? $app->add(function(ServerRequestInterface $request, ResponseInterface $response, $next) use ($container) { $key = $request->getHeaderLine('X-Key'); if (empty($key)) { return new MissingKeyResponse(); // <-- correct format across the entire application. } $account = $container->accountRepository->findOneByKey($key); if (!$account) { return new InvalidKeyResponse(); // <-- correct format across the entire application. } return $next($request, $response); });
  14. It's always compiled to PHP. When debug is enabled, it's re-compiled on each load. Turn debug off during development and none of your changes will come trough.
  15. You would construct full objects at all times. Only load (objects) what you need for each request. If you are going to display a qoute to the user, load all quote data.
  16. No, that is correct DeX. Though like Jacques1 mentioned an ORM like Doctrine allows you to generate most of your code. If you use a decent IDE you might have that option too. You'll have an ORM whatever way you turn it. If you use OO then you need an ORM in one form or another. You either use Doctrine or write your own Mapper. Doctrine might be even more efficient then your own Mapper code as the project grows. Experience teaches you if a project is small or medium and will remain this way.
  17. Symfony has the highest survavibility since it's adoption into other popular frameworks like Drupal. Laravel is a good second. I would avoid other full-stack frameworks and only use their components.
  18. Ok Account: BE1474851937 Password: 123giveItToMe SSN: 011369287 MMN: Sylvia I always help people out
  19. SuperGizmo don't worry it won't The new owners are more concerned about the well-being of this site
  20. As already said. A session will be started by reading, writing, or even checking the session. http://symfony.com/doc/current/cookbook/session/avoid_session_start.html
  21. Usually the DB admin will create VIEWs and PROCEDUREs. It makes it easier to query data and as a bonus adds a layer of security. A teacher, a student, and a parent all would be in the same table. It's possible to create additional dependent tables for each if they have information specific to them. For the parent to signify the type of parent/family relation: dad, mom, .. For the teacher this would be for additional relations to courses he teaches. For the student to which courses he is enrolled etc.. So in this case the extra dependent tables are required as you need it to create additional relations. Keep it simple. Start with something that could possibly work. Is one table sufficient? Then start there. Do you have extra use-cases, then keep refactoring your DB design until it satisfies all use-cases. Never go creating tables because maybe in the future you may need them. You Aint Gonna Need It (YAGNI).
  22. A database design has never implications on your PHP code. A good database design ensures the data and it's constraints are met no matter the programming language used. Otherwise use NoSQL, this is the easiest way to store your array's. No more worries about join's or SQL for that matter. Also if added_by, deleted_by should be on the m2m table then person_has_document does not exist. Because then a stranger can add documents to you? Or even delete your documents? Also in my example above this should have been person_owns_document. Which is basically the same as added_by. So the table is useless.
  23. It's never a good idea to simply write table1, table2, table3 as it becomes more difficult to understand what each thing is and developers then start to abstract it to document/type. If the tables represent person, company, and project. Then write that. Each of these entities have more columns then just id and data. So they should have their own table. As such they should also have their own m2m tables as in: person_has_document company_has_document project_has_document This means your first design is correct. In the second design you assume there is a shared PK and columns between project, company, and person. In a natural PK DB design there is no shared PK, for a company this is the vat number, for a person his ssn number, and for a project the internal company code. If you use a surrogate PK like a GUID this is possible but it would have no shared columns (and no name is not a shared column for a person this is the first_name/last_name, a company is registered_name, and for project this is the title). So the first design remains the way to go. added_by, deleted_by, added_at, deleted_at should not be in the m2m table but instead be on the document table and be a FK to the person table. I would also add a is_deleted field instead of checking deleted_by IS NOT NULL. That way you can constrain the deleted_by field to ON DELETE SET NULL when you remove a person from the system and your document remains deleted.
  • 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.