Jump to content

ignace

Moderators
  • Posts

    6,457
  • Joined

  • Last visited

  • Days Won

    26

Everything posted by ignace

  1. It would help to post the PHP code. Also mostly these are cut-and-paste/works-out-of-the-box type of code. Since you have a website I assume you have at least a little technical background? And I assume your server supports PHP? Have you tried creating a new file pasting in the code and see what it outputs?
  2. The difference is that in my case the Mapper has context so related objects are also loaded. While your chained method calls need to be called every time you need more data, resulting in more queries. Also because your Entity uses the DAO directly it is actually tied to your DB which has the dependency the wrong way like an object created by a Factory object would be tied to its creator. These should be decoupled. As to why the Mapper I link to this article: http://stackoverflow.com/a/814479
  3. Yes, you went too far. Also your business classes should not be aware of DB classes. If your Product needs a Motor then make sure your DAO injects it. class ProductMapper { private $SQL_LOAD_PRODUCT_WITH_MOTOR = 'SELECT .. FROM .. JOIN .. WHERE ..'; public function someBusinessIdentifiableMethod() { $rows = db_query($this->SQL_LOAD_PRODUCT_WITH_MOTOR, [..]); foreach ($rows as $row) { $products[] = $product = new Product(); //.. $product->setMotor($motor = new Motor()); } return $products; } }Your Mapper class would query your database and then "map" the result to objects. Much like the ResultSetMapping from Doctrine.
  4. Wow, my worst days are like accidentally wipe clients database with no back-up. But everyone has done that once or twice. And that's about it
  5. There is no reason you should have one class that does and knows everything. It's far better to create specialised classes for your pages. It's also far better for performance as you'll quickly be loading too many unneeded classes.
  6. As a side note I want to point out that MVC does not mean create a Model class, a Controller class, and a View class. It means separation of concerns, where the important bits are Model (Business Logic) and View (Presentation Logic). The Controller is the part that binds these two together. A simple (W)MVC example would be a contact page: // contact.php if (isset($_POST['submit'])) { mail('hello@some.website', $_POST['subject'], $_POST['message']); } include 'views/contact.html';Believe it or not, but this is really an (W)MVC implementation. Where: - contact.php -- is the controller (model and view are unaware of each other, yet together they achieve a specific goal, contacting the owner) - mail() -- is the model (hiding how mailing works and providing a simple interface) - views/contact.html -- is the view (hiding the interface how the user will enter the required data) - Apache -- is the frontController (like your App.php, translate a route to a controller) It's important that the model is unaware of the view and the controller otherwise it would severely limit it's re-use. Your LoginModel is also not correct because it violates the Single Responsibility Principle. It finds a user based on user and pass, and it stores a session cookie. You'll also have to duplicate the "find a user based on user and pass" every time you need it thus violating the Don't Repeat Yourself principle. class UserMapper .. { private $pdo; function __construct(PDO $pdo) { $this->pdo = $pdo; } function findByNameAndPassword($username, $password) { // implementation } } class Session .. { function set($key, $value, $lifetime) { // implementation } function get($key, $default = null) { // implementation } } class LoginService { private $userMapper; private $session; function login($username, $password) { // $user = $this->userMapper->findByNameAndPassword($username, $password); // if (null !== $user) { // $this->session->set('loggedin', true, 86400); // } } function isLoggedIn() { return $this->session->get('loggedin', false); } }As an example. You would use the LoginService in your controller as it hides how a user is logged in and is completely re-usable in another project if needed. Controller and View are considered to be project specific and thus (mostly) throw-away-code. The Model however not so much. This kind of programming will help keep your controllers thin and your (model) code re-usable.
  7. I would definitely tell them. Not sure what the laws are in the US but I haven't run in any trouble so far. It's not like your hacking their software plus it gives them an incentive to choose you now and in the future. I found they can appreciate someone with proper knowledge of things, someone they can trust and build their business on. That's what you do as a freelancer after all, build relations.
  8. Not sure what the idea is but it misses several files and it appears that whoever uses it is forced to a certain layout with no configuration available. Care to elaborate what the idea here is? And what do you mean by freedom from cms? My first thought is something like a static site generator.
  9. Barand asked you to do this: $questions = ThemexUser::$data['user']['profile']; var_export($questions);And put the output here.
  10. Here's an example of a class that does the same thing but keeps testability in mind: Request.php
  11. Then it was exposed and not encapsulated. But whatever, it does not matter, if I as a client can stop a monster from attacking as long as calls to the server (for example to award XP and such) are protected against malicious use, none of that matters. I don't care if you can make your character a lvl 49 on your screen as long as it does not persist on the server, I don't care.
  12. It's better to only instance objects when you need them. Each object instance consumes memory and each PHP instance is limited to a certain amount of memory. Keeping memory consumption low means more PHP instances can be spawned and thus more requests can be handled which increases performance.
  13. Use AJAX to validate the answer, if it's wrong stop playing otherwise proceed.
  14. It all depends on how you want your game to work of course but the initializing code could be something like: $map = Map::fromBase64('BggEBgYGCQkGCQkQAAAAAKiqqip4VVUtWFVVJVhVVSVYVVUlWFVVJViVViVYlVYlWFVVJVhVVSVYVVUlWFVVJXhVVS2oqqoqAAAAAA=='); // or load it from a file $map = Map::fromFile('/path/to/some.map'); $player = new Player('Player 1'); $game = new CliGame($player, $map, new CliManager(/*read input, colorize output and whatnot*/)); $game->execute();execute() simply executes the game for cli it uses the CLI commands, for web it uses HTML and GET/POST. The doMove() translates the move to a Tile. WWWWWWWWW WXXOWXXXW WWWXXXXXW WWWWWWWWW X (=row) and Y (=column) thus (1,3) would return O (or Tile::PLAYER). Since Map holds all the Tile's it's the one that translates the Move to a Tile so the Game can make decisions based on the player action.
  15. It's a good start but OOP-wise it's still lacking quite a few things. Your data is not encapsulated (using public instead of private). Your classes have too many responsibilities (loading a map, running the game, etc..). Wrong use of inheritance (ie cliClass extends Sokoban). Some key concepts are defined by array's instead of classes or are simple functions. Instead of creating a function mapFromBase, create a class Map with a static function fromBase64: class Map { public function __construct(array $tiles) { $this->tiles = $tiles; } public static function fromString($str, $sep = "\n") { $tiles = [[]]; $row = 0; foreach (str_split($str) as $char) { if ($sep === $char) { $tiles[$row++] = array(); continue; } $tiles[$row][] = $char; } return new static($tiles); } public static function fromBase64($str) { return static::fromString( base64_decode($str) ); } public static function fromFile(SplFileInfo $file) { return static::fromString( file_get_contents($file->getPathName()) ); } } // Map::fromBase64('Hz8meZ..'); // Map::fromString('O . '); // ...Now instead of loadMap you can simply do setMap(Map $map) or pass it through the constructor __construct(Map $map). Also define a Player class to hold all player info. For the movement define a Move class and Tile like so: class Move { const UP = 0; const RIGHT = 1; const DOWN = 2; const LEFT = 3; public static function fromString($str) { if (false === $pos = array_search(['n', 'e', 's', 'w'], $str)) { throw new InvalidMoveException(); } return $pos; } } class Tile { // different kind of tiles on the map const TYPE_STONE = 'O'; const TYPE_EMPTY = ' '; const TYPE_WALL = '|'; const TYPE_PLAYER = '.'; } // the actual map (holding all tiles) class Map .. { public function doMove($move) { // $map->doMove(Move::UP); // translate move to direction // validate (throw exception if invalid move for example Tile::TYPE_WALL or map bounds) // execute return $this->tiles[$x][$y]; // returns Tile::TYPE_* } }To encapsulate everything create a Game class that holds the map, the player, the state of the current game, etc.. // abstract Game for different implementations abstract class Sokoban { const STATE_NEW = 0; const STATE_STARTED = 1; const STATE_RUNNING = 3; const STATE_ENDED_WON = 2; const STATE_ENDED_LOST = -1; private $player; private $map; private $state = self::STATE_NEW; abstract public function execute(); } // cli implementation of Game class CliSokoban extends Sokoban { public function execute() { $input = Move::fromString($this->readInput()); $tile = $this->map->doMove($input); // .. } }By defining all your concepts it makes it easier to understand your program and easier to maintain.
  16. If you are using HTML5 and the <video/> tag you can use the Video API. Otherwise if you are using YouTube or Vimeo they both expose their own API.
  17. Why would you instantiate a Account object inside Customer? A customer can have multiple Accounts with different banks. And Account should not instantiate a BankCard object either since not all Accounts come with a BankCard, sometimes you have to pay for them. Understanding OO is understanding how your application works. A bank screens a customer before opening an account and may decline if certain criteria aren't met (for example good credit rating). Which is also why you run into trouble passing required arguments to these classes simply because they do not belong there. class Bank { public function createNewAccountFor(Customer $customer) { $this->assertCustomerMeetsCriteria($customer); $account = new Account($this, $customer); // the bank decides how new Account's are created $account->queue(new NewAccountFee($this)); // and makes sure it applies it's own rules and those of the government $account->addEventListener('deposit', new FiftyPercentYouMakeBelongsToTheGovernmentTax()); return $account; } }
  18. Not too up on PHP it seems either. That's 12+ years old code (look Mom, a dinosaur!). var is now public but this violates the encapsulation and should really be private. The old style constructor function RSSFeed() which was popular in PHP 4.0 but is now replaced by __construct in PHP5 (released in 2003). And we are going to need the exact error as the code itself runs fine. PS No offense intended, but that code is really old!
  19. No they can't stop it from attacking, only if you expose the function/variable that is responsible for this. function Monster() { var attacking = true; this.printAttackingState = function() { alert('attacking = ' + (attacking ? 'true' : 'false')); } } var monster = new Monster(); monster.attacking = false; monster.printAttackingState(); // attacking = trueYou can't change the attacking variable simply because it's not exposed.
  20. You don't have to do anything yet server-side. Write your game in JavaScript and use events to have monsters spawn and auto-attack. Spawn monsters outside of the visible area, when the player moves close enough have the monster attack the player.
  21. Packagist is a web interface to the packages available on composer. You can search for packages just like with composer search and then composer browse. If you develop packages yourself you use it to register and manage your packages.
  22. There are already alternatives to PMA. Find the web-based ones here: http://alternativeto.net/software/phpmyadmin/?platform=online
  23. <?php function get_random_word(PDO $pdo) { $sql = 'SELECT word FROM words WHRE id = (SELECT FLOOR(MIN(id) + RAND() * (MAX(id) - MIN(id))) FROM words)'; $stmt = $pdo->query($sql); if (0 === $stmt->rowCount()) { return ''; } return $stmt->fetchColumn(); } $pdo = new PDO('dsn', 'user', 'pass'); $letters = str_split(get_random_word($pdo)); ?> <html> <head><title>Word Game</title></head> <body> <div class="letters"> <span class="letter"><?php implode('</span><span class="letter">', $letters); ?></span> </div> <script src="//code.jquery.com/jquery-2.1.3.min.js"></script> <script> $(function() { var $dragging = null; $(document.body).on('mousemove', function(e) { if ($dragging) { $dragging.offset({ top: e.pageY, left: e.pageX }); } }).on('mousedown', 'span.letter', function(e) { $dragging = $(e.target); }).on('mouseup', function(e) { $dragging = null; }); }); </script> </body> </html>
  24. Singleton is an anti-pattern instead pass the DB object to your User_DataManager through the constructor. Use a dependency injection container to resolve your dependencies. You can use Pimple for example. This allows you to decouple your query builder DB from the actual database connection PDO. $pimple['config_file'] = '/path/to/config.ini'; $pimple['config'] = function($c) { return Config::fromIni($c['config_file']); }; $pimple['db'] = function($c) { $cfg = $c['config']; $db = $cfg['db']; return new PDO($db['dsn'], $db['user'], $db['pass'], $db['options']); }; $pimple['query_builder'] = function($c) { return new DB($c['db']); };Also avoid using die() inside classes and instead use exceptions which can be handled and formatted in a nice 404 or 503 message to the user.
×
×
  • 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.