├── .htaccess ├── CHANGELOG.md ├── README.md ├── _tutorial ├── donate-with-flattr.png ├── donate-with-paypal.png ├── tutorial-part-01.png ├── tutorial-part-02.png ├── tutorial-part-03.png ├── tutorial-part-04.png └── tutorial-part-05.png ├── application ├── .htaccess ├── _install │ ├── 01-create-database.sql │ ├── 02-create-table-song.sql │ └── 03-insert-demo-data-into-table-song.sql ├── config │ └── config.php ├── controller │ ├── home.php │ └── songs.php ├── libs │ ├── application.php │ └── controller.php ├── models │ ├── songsmodel.php │ └── statsmodel.php └── views │ ├── _templates │ └── base.twig │ ├── home │ ├── example_one.twig │ ├── example_two.twig │ └── index.twig │ └── songs │ └── index.twig ├── composer.json ├── index.php └── public ├── css └── style.css ├── img └── demo-image.png ├── js └── application.js └── scss ├── modules └── _colors.scss └── style.scss /.htaccess: -------------------------------------------------------------------------------- 1 | # Necessary to prevent problems when using a controller named "index" and having a root index.php 2 | # more here: http://httpd.apache.org/docs/2.2/content-negotiation.html 3 | Options -MultiViews 4 | 5 | # Activates URL rewriting (like myproject.com/controller/action/1/2/3) 6 | RewriteEngine On 7 | 8 | # Disallows others to look directly into /public/ folder 9 | Options -Indexes 10 | 11 | # When using the script within a sub-folder, put this path here, like /mysubfolder/ 12 | # If your app is in the root of your web folder, then leave it commented out 13 | RewriteBase /php-mvc-advanced/ 14 | 15 | # General rewrite rules 16 | RewriteCond %{REQUEST_FILENAME} !-d 17 | RewriteCond %{REQUEST_FILENAME} !-f 18 | RewriteCond %{REQUEST_FILENAME} !-l 19 | 20 | RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGE LOG 2 | ========== 3 | 4 | **January 4th 2014** 5 | - fixed htaccess issue when there's a controller named "index" and a base index.php (which collide) 6 | 7 | **December 29th 2013** 8 | - fixed case-sensitive model file loading (thanks "grrnikos") 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-MVC-ADVANCED 2 | 3 | *Note: This is the same like [panique/php-mvc](https://github.com/panique/php-mvc), but with additional features.* 4 | *This repo is in development, more to come...* 5 | 6 | ### New in the advanced version: 7 | 8 | 1. Twig 9 | 2. SASS-compiler in PHP ! The SASS compiling is optional, you can delete the scss folder and just use classic .css, too. 10 | I've used https://github.com/panique/laravel-sass here. 11 | 12 | An extremely simple and easy to understand MVC skeleton application, reduced to the max. 13 | Everything is **as simple as possible**, as **manually as possible** and as readable as possible. 14 | This project is - by intention - NOT a full framework, it's a bare-bone structure, written in 15 | purely native PHP ! The php-mvc skeleton tries to be the extremely slimmed down opposite of big frameworks 16 | like Zend2, Symfony or Laravel. 17 | 18 | [![Donate with PayPal banner](_tutorial/donate-with-paypal.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=P5YLUK4MW3LDG) 19 | 20 | [![Donate with Flattr banner](_tutorial/donate-with-flattr.png)](https://flattr.com/submit/auto?user_id=panique&url=https%3A%2F%2Fgithub.com%2Fpanique%2Fphp-mvc-advanced) 21 | 22 | ## Why does this project exist ? 23 | 24 | One of the biggest question in the PHP world is "How do I build an application ?". 25 | It's hard to find a good base, a good file structure and useful information on that, but at the same time 26 | there are masses of frameworks that might be really good, but really hard to understand, hard to use and extremely 27 | complex. This project tries to be some kind of naked skeleton bare-bone for quick application building, 28 | especially for the not-so-advanced coder. 29 | 30 | ### Goals of this project: 31 | 32 | - give people a clean base MVC structure to build a modern PHP application with 33 | - teach people the basics of the Model-View-Controller architecture 34 | - encourage people to code according to PSR 1/2 coding guidelines 35 | - promote the usage of PDO 36 | - promote the usage of external libraries via Composer 37 | - promote development with max. error reporting 38 | - promote to comment code 39 | - promote the usage of OOP code 40 | - using only native PHP code, so people don't have to learn a framework 41 | 42 | ## Support forum 43 | 44 | If you are stuck with something even AFTER reading and following the install tutorials and the quick-manual, then feel 45 | free to ask in the [official forum](http://forum.php-mvc.net/). Note that this forum is fresh and new, more content 46 | will come over time. 47 | 48 | ## Installation 49 | 50 | 1. First, install Composer ([How to install Composer on Ubuntu, Debian or Windows 7/8](http://www.dev-metal.com/install-update-composer-windows-7-ubuntu-debian-centos/)). 51 | That's some kind of PHP standard now and there's no reason to work without Composer. If you think "I don't need/want 52 | Composer" then you are doing something seriously wrong! 53 | 54 | 2. Copy this repo into a public accessible folder on your server. 55 | Common techniques are a) downloading and extracting the .zip / .tgz by hand, b) cloning the repo with git (into var/www) 56 | 57 | ``` 58 | git clone https://github.com/panique/php-mvc-advanced.git /var/www 59 | ``` 60 | 61 | or c) getting the repo via Composer (here we copy into var/www) 62 | 63 | ``` 64 | composer create-project panique/php-mvc-advanced /var/www dev-master 65 | ``` 66 | 67 | 3. Install mod_rewrite, for example by following this guideline: 68 | [How to install mod_rewrite in Ubuntu](http://www.dev-metal.com/enable-mod_rewrite-ubuntu-12-04-lts/) 69 | 70 | 4. Run the SQL statements in the *application/_install* folder. 71 | 72 | 5. Change the .htaccess file from 73 | ``` 74 | RewriteBase /php-mvc-advanced/ 75 | ``` 76 | to where you put this project, relative to the web root folder (usually /var/www). So when you put this project into 77 | the web root, like directly in /var/www, then the line should look like or can be commented out: 78 | ``` 79 | RewriteBase / 80 | ``` 81 | If you have put the project into a sub-folder, then put the name of the sub-folder here: 82 | ``` 83 | RewriteBase /sub-folder/ 84 | ``` 85 | 86 | 6. Edit the *application/config/config.php*, change this line 87 | ```php 88 | define('URL', 'http://127.0.0.1/php-mvc-advanced/'); 89 | ``` 90 | to where your project is. Real domain, IP or 127.0.0.1 when developing locally. Make sure you put the sub-folder 91 | in here (when installing in a sub-folder) too, also don't forget the trailing slash ! 92 | 93 | 7. Edit the *application/config/config.php*, change these lines 94 | ```php 95 | define('DB_TYPE', 'mysql'); 96 | define('DB_HOST', '127.0.0.1'); 97 | define('DB_NAME', 'php-mvc'); 98 | define('DB_USER', 'root'); 99 | define('DB_PASS', 'mysql'); 100 | ``` 101 | to your database credentials. If you don't have an empty database, create one. Only change the type `mysql` if you 102 | know what you are doing. 103 | 104 | 8. Run `composer install` on the command line while being in the root of your project. 105 | 106 | ## A quickstart tutorial 107 | 108 | You can also find these tutorial pictures in the *_tutorial* folder. 109 | **Note:** **These files are not up-to-date, as Twig and SASS support are not mentioned here. I'll update the tutorial 110 | when there's time.** 111 | 112 | ![php-mvc introduction tutorial - page 1](_tutorial/tutorial-part-01.png) 113 | ![php-mvc introduction tutorial - page 2](_tutorial/tutorial-part-02.png) 114 | ![php-mvc introduction tutorial - page 3](_tutorial/tutorial-part-03.png) 115 | ![php-mvc introduction tutorial - page 4](_tutorial/tutorial-part-04.png) 116 | ![php-mvc introduction tutorial - page 5](_tutorial/tutorial-part-05.png) 117 | 118 | ## You like what you see ? 119 | 120 | Then please also have a look on ... 121 | 122 | #### My other project php-login 123 | 124 | A collection of 4 similar login scripts for PHP, from a super-simple one-file 125 | script with a SQLite one-file to a highly professional MVC frameworks solution. All scripts use the most advanced 126 | hashing algorithms possible in PHP, exactly like the PHP core developers want you to use them. 127 | 128 | https://github.com/panique/php-login (full MVC framework) 129 | 130 | https://github.com/panique/php-login-minimal (minimal) 131 | 132 | https://github.com/panique/php-login-advanced (advanced) 133 | 134 | https://github.com/panique/php-login-one-file (one-file) 135 | 136 | #### My PHP and frontend blog 137 | 138 | Lots of non-boring development stuff and tutorials there. 139 | 140 | http://www.dev-metal.com 141 | 142 | ## Useful information 143 | 144 | 1. SQLite does not have a rowCount() method (!). Keep that in mind in case you use SQLite. 145 | 146 | 2. Don't use the same name for class and method, as this might trigger an (unintended) *__construct* of the class. 147 | This is really weird behaviour, but documented here: [php.net - Constructors and Destructors](http://php.net/manual/en/language.oop5.decon.php). 148 | 149 | ## Add external libraries via Composer 150 | 151 | To add external libraries/tools/whatever into your project in an extremely clean way, simply add a line with the 152 | repo name and version to the composer.json! Take a look on these tutorials if you want to get into Composer: 153 | [How to install (and update) Composer on Windows 7 or Ubuntu / Debian](http://www.dev-metal.com/install-update-composer-windows-7-ubuntu-debian-centos/) 154 | and [Getting started with Composer](http://www.dev-metal.com/getting-started-composer/). 155 | 156 | ## License 157 | 158 | This project is licensed under the MIT License. 159 | This means you can use and modify it for free in private or commercial projects. 160 | 161 | ## Contribute 162 | 163 | Please commit into the develop branch (which holds the in-development version), not into master branch 164 | (which holds the tested and stable version). 165 | 166 | ## Support / Donate 167 | 168 | If you think this script is useful and saves you a lot of work, then think about supporting the project: 169 | 170 | 1. Donate via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=P5YLUK4MW3LDG), [GitTip](https://www.gittip.com/Panique/) or [Flattr](https://flattr.com/submit/auto?user_id=panique&url=https%3A%2F%2Fgithub.com%2Fpanique%2Fphp-mvc-advanced). 171 | 2. Rent your next server at [A2 Hosting](http://www.a2hosting.com/4471.html) or [DigitalOcean](https://www.digitalocean.com/?refcode=40d978532a20). 172 | 3. Contribute to this project. Feel free to improve this project with your skills. 173 | 4. Spread the word: Tell others about this project. 174 | 175 | ## Linked music tracks in the demo application 176 | 177 | The linked tracks in this naked application are just some of my personal favourites of the last few months. 178 | I think it's always a good idea to fill boring nerd-code stuff with quality culture. 179 | -------------------------------------------------------------------------------- /_tutorial/donate-with-flattr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SourceCode/php-mvc-advanced/ce4b0cf48f7be5ec60823a1c59ed62e99cb38933/_tutorial/donate-with-flattr.png -------------------------------------------------------------------------------- /_tutorial/donate-with-paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SourceCode/php-mvc-advanced/ce4b0cf48f7be5ec60823a1c59ed62e99cb38933/_tutorial/donate-with-paypal.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SourceCode/php-mvc-advanced/ce4b0cf48f7be5ec60823a1c59ed62e99cb38933/_tutorial/tutorial-part-01.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SourceCode/php-mvc-advanced/ce4b0cf48f7be5ec60823a1c59ed62e99cb38933/_tutorial/tutorial-part-02.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SourceCode/php-mvc-advanced/ce4b0cf48f7be5ec60823a1c59ed62e99cb38933/_tutorial/tutorial-part-03.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SourceCode/php-mvc-advanced/ce4b0cf48f7be5ec60823a1c59ed62e99cb38933/_tutorial/tutorial-part-04.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SourceCode/php-mvc-advanced/ce4b0cf48f7be5ec60823a1c59ed62e99cb38933/_tutorial/tutorial-part-05.png -------------------------------------------------------------------------------- /application/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Order Deny,Allow 3 | Deny from all 4 | 5 | -------------------------------------------------------------------------------- /application/_install/01-create-database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS `php-mvc`; 2 | -------------------------------------------------------------------------------- /application/_install/02-create-table-song.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `php-mvc`.`song` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `artist` text COLLATE utf8_unicode_ci NOT NULL, 4 | `track` text COLLATE utf8_unicode_ci NOT NULL, 5 | `link` text COLLATE utf8_unicode_ci, 6 | PRIMARY KEY (`id`), 7 | UNIQUE KEY `id` (`id`) 8 | ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 9 | -------------------------------------------------------------------------------- /application/_install/03-insert-demo-data-into-table-song.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `php-mvc`.`song` (`id`, `artist`, `track`, `link`) VALUES 2 | (1, 'Dena', 'Cash, Diamond Ring, Swimming Pools', 'http://www.youtube.com/watch?v=r4CDc9yCAqE'), 3 | (2, 'Jessy Lanza', 'Kathy Lee', 'http://vimeo.com/73455369'), 4 | (3, 'The Orwells', 'In my Bed (live)', 'http://www.youtube.com/watch?v=8tA_2qCGnmE'), 5 | (4, 'L''Orange & Stik Figa', 'Smoke Rings', 'https://www.youtube.com/watch?v=Q5teohMyGEY'), 6 | (5, 'Labyrinth Ear', 'Navy Light', 'http://www.youtube.com/watch?v=a9qKkG7NDu0'), 7 | (6, 'Bon Hiver', 'Wolves (Kill them with Colour Remix)', 'http://www.youtube.com/watch?v=5GXAL5mzmyw'), 8 | (7, 'Detachments', 'Circles (Martyn Remix)', 'http://www.youtube.com/watch?v=UzS7Gvn7jJ0'), 9 | (8, 'Dillon & Dirk von Loetzow', 'Tip Tapping (Live at ZDF Aufnahmezustand)', 'https://www.youtube.com/watch?v=hbrOLsgu000'), 10 | (9, 'Dillon', 'Contact Us (Live at ZDF Aufnahmezustand)', 'https://www.youtube.com/watch?v=E6WqTL2Up3Y'), 11 | (10, 'Tricky', 'Hey Love (Promo Edit)', 'http://www.youtube.com/watch?v=OIsCGdW49OQ'), 12 | (11, 'Compuphonic', 'Sunset feat. Marques Toliver (DJ T. Remix)', 'http://www.youtube.com/watch?v=Ue5ZWSK9r00'), 13 | (12, 'Ludovico Einaudi', 'Divenire (live @ Royal Albert Hall London)', 'http://www.youtube.com/watch?v=X1DRDcGlSsE'), 14 | (13, 'Maxxi Soundsystem', 'Regrets we have no use for (Radio1 Rip)', 'https://soundcloud.com/maxxisoundsystem/maxxi-soundsystem-ft-name-one'), 15 | (14, 'Beirut', 'Nantes (Fredo & Thang Remix)', 'https://www.youtube.com/watch?v=ojV3oMAgGgU'), 16 | (15, 'Buku', 'All Deez', 'http://www.youtube.com/watch?v=R0bN9JRIqig'), 17 | (16, 'Pilocka Krach', 'Wild Pete', 'http://www.youtube.com/watch?v=4wChP_BEJ4s'), 18 | (17, 'Mount Kimbie', 'Here to stray (live at Pitchfork Music Festival Paris)', 'http://www.youtube.com/watch?v=jecgI-zEgIg'), 19 | (18, 'Kool Savas', 'King of Rap (2012) / Ein Wunder', 'http://www.youtube.com/watch?v=mTqc6UTG1eY&hd=1'), 20 | (19, 'Chaim feat. Meital De Razon', 'Love Rehab (Original Mix)', 'http://www.youtube.com/watch?v=MJT1BbNFiGs'), 21 | (20, 'Emika', 'Searching', 'http://www.youtube.com/watch?v=oscuSiHfbwo'), 22 | (21, 'Emika', 'Sing to me', 'http://www.youtube.com/watch?v=k9sDBZm8pgk'), 23 | (22, 'George Fitzgerald', 'Thinking of You', 'http://www.youtube.com/watch?v=-14B8l49iKA'), 24 | (23, 'Disclosure', 'You & Me (Flume Edit)', 'http://www.youtube.com/watch?v=OUkkaqSNduU'), 25 | (24, 'Crystal Castles', 'Doe Deer', 'http://www.youtube.com/watch?v=zop0sWrKJnQ'), 26 | (25, 'Tok Tok vs. Soffy O.', 'Missy Queens Gonna Die', 'http://www.youtube.com/watch?v=EN0Tnw5zy6w'), 27 | (26, 'Fink', 'Maker (Synapson Remix)', 'http://www.youtube.com/watch?v=Dyd-cUkj4Nk'), 28 | (27, 'Flight Facilities (ft. Christine Hoberg)', 'Clair De Lune', 'http://www.youtube.com/watch?v=Jcu1AHaTchM'), 29 | (28, 'Karmon', 'Turning Point (Original Mix)', 'https://www.youtube.com/watch?v=-tB-zyLSPEo'), 30 | (29, 'Shuttle Life', 'The Birds', 'http://www.youtube.com/watch?v=-I3m3cWDEtM'), 31 | (30, 'Santé', 'Homegirl (Rampa Mix)', 'http://www.youtube.com/watch?v=fnhMNOWxLYw'); 32 | -------------------------------------------------------------------------------- /application/config/config.php: -------------------------------------------------------------------------------- 1 | render('home/index'); 20 | } 21 | 22 | /** 23 | * PAGE: exampleone 24 | * This method handles what happens when you move to http://yourproject/home/exampleone 25 | * The camelCase writing is just for better readability. The method name is case insensitive. 26 | */ 27 | public function exampleOne() 28 | { 29 | $this->render('home/example_one'); 30 | } 31 | 32 | /** 33 | * PAGE: exampletwo 34 | * This method handles what happens when you move to http://yourproject/home/exampletwo 35 | * The camelCase writing is just for better readability. The method name is case insensitive. 36 | */ 37 | public function exampleTwo() 38 | { 39 | $this->render('home/example_two'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /application/controller/songs.php: -------------------------------------------------------------------------------- 1 | loadModel('SongsModel'); 23 | $songs = $songs_model->getAllSongs(); 24 | 25 | // load another model, perform an action, pass the returned data to a variable 26 | // NOTE: please write the name of the model "LikeThis" 27 | $stats_model = $this->loadModel('StatsModel'); 28 | $amount_of_songs = $stats_model->getAmountOfSongs(); 29 | 30 | // render the view, pass the data 31 | $this->render('songs/index', array('songs' => $songs, 'amount_of_songs' => $amount_of_songs)); 32 | } 33 | 34 | /** 35 | * ACTION: addSong 36 | * This method handles what happens when you move to http://yourproject/songs/addsong 37 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "add a song" form on songs/index 38 | * directs the user after the form submit. This method handles all the POST data from the form and then redirects 39 | * the user back to songs/index via the last line: header(...) 40 | * This is an example of how to handle a POST request. 41 | */ 42 | public function addSong() 43 | { 44 | // if we have POST data to create a new song entry 45 | if (isset($_POST["submit_add_song"])) { 46 | // load model, perform an action on the model 47 | $songs_model = $this->loadModel('SongsModel'); 48 | $songs_model->addSong($_POST["artist"], $_POST["track"], $_POST["link"]); 49 | } 50 | 51 | // where to go after song has been added 52 | header('location: ' . URL . 'songs/index'); 53 | } 54 | 55 | /** 56 | * ACTION: deleteSong 57 | * This method handles what happens when you move to http://yourproject/songs/deletesong 58 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "delete a song" button on songs/index 59 | * directs the user after the click. This method handles all the data from the GET request (in the URL!) and then 60 | * redirects the user back to songs/index via the last line: header(...) 61 | * This is an example of how to handle a GET request. 62 | * @param int $song_id Id of the to-delete song 63 | */ 64 | public function deleteSong($song_id) 65 | { 66 | // if we have an id of a song that should be deleted 67 | if (isset($song_id)) { 68 | // load model, perform an action on the model 69 | $songs_model = $this->loadModel('SongsModel'); 70 | $songs_model->deleteSong($song_id); 71 | } 72 | 73 | // where to go after song has been deleted 74 | header('location: ' . URL . 'songs/index'); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /application/libs/application.php: -------------------------------------------------------------------------------- 1 | splitUrl(); 28 | 29 | // check for controller: does such a controller exist ? 30 | if (file_exists('./application/controller/' . $this->url_controller . '.php')) { 31 | 32 | // if so, then load this file and create this controller 33 | // example: if controller would be "car", then this line would translate into: $this->car = new car(); 34 | require './application/controller/' . $this->url_controller . '.php'; 35 | $this->url_controller = new $this->url_controller(); 36 | 37 | // check for method: does such a method exist in the controller ? 38 | if (method_exists($this->url_controller, $this->url_action)) { 39 | 40 | // call the method and pass the arguments to it 41 | if (isset($this->url_parameter_3)) { 42 | // will translate to something like $this->home->method($param_1, $param_2, $param_3); 43 | $this->url_controller->{$this->url_action}($this->url_parameter_1, $this->url_parameter_2, $this->url_parameter_3); 44 | } elseif (isset($this->url_parameter_2)) { 45 | // will translate to something like $this->home->method($param_1, $param_2); 46 | $this->url_controller->{$this->url_action}($this->url_parameter_1, $this->url_parameter_2); 47 | } elseif (isset($this->url_parameter_1)) { 48 | // will translate to something like $this->home->method($param_1); 49 | $this->url_controller->{$this->url_action}($this->url_parameter_1); 50 | } else { 51 | // if no parameters given, just call the method without parameters, like $this->home->method(); 52 | $this->url_controller->{$this->url_action}(); 53 | } 54 | } else { 55 | // default/fallback: call the index() method of a selected controller 56 | $this->url_controller->index(); 57 | } 58 | } else { 59 | // invalid URL, so simply show home/index 60 | require './application/controller/home.php'; 61 | $home = new Home(); 62 | $home->index(); 63 | } 64 | } 65 | 66 | /** 67 | * Get and split the URL 68 | */ 69 | private function splitUrl() 70 | { 71 | if (isset($_GET['url'])) { 72 | 73 | // split URL 74 | $url = rtrim($_GET['url'], '/'); 75 | $url = filter_var($url, FILTER_SANITIZE_URL); 76 | $url = explode('/', $url); 77 | 78 | // Put URL parts into according properties 79 | // By the way, the syntax here is just a short form of if/else, called "Ternary Operators" 80 | // @see http://davidwalsh.name/php-shorthand-if-else-ternary-operators 81 | $this->url_controller = (isset($url[0]) ? $url[0] : null); 82 | $this->url_action = (isset($url[1]) ? $url[1] : null); 83 | $this->url_parameter_1 = (isset($url[2]) ? $url[2] : null); 84 | $this->url_parameter_2 = (isset($url[3]) ? $url[3] : null); 85 | $this->url_parameter_3 = (isset($url[4]) ? $url[4] : null); 86 | 87 | // for debugging. uncomment this if you have problems with the URL 88 | // echo 'Controller: ' . $this->url_controller . '
'; 89 | // echo 'Action: ' . $this->url_action . '
'; 90 | // echo 'Parameter 1: ' . $this->url_parameter_1 . '
'; 91 | // echo 'Parameter 2: ' . $this->url_parameter_2 . '
'; 92 | // echo 'Parameter 3: ' . $this->url_parameter_3 . '
'; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /application/libs/controller.php: -------------------------------------------------------------------------------- 1 | openDatabaseConnection(); 20 | } 21 | 22 | /** 23 | * Open the database connection with the credentials from application/config/config.php 24 | */ 25 | private function openDatabaseConnection() 26 | { 27 | // set the (optional) options of the PDO connection. in this case, we set the fetch mode to 28 | // "objects", which means all results will be objects, like this: $result->user_name ! 29 | // For example, fetch mode FETCH_ASSOC would return results like this: $result["user_name] ! 30 | // @see http://www.php.net/manual/en/pdostatement.fetch.php 31 | $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING); 32 | 33 | // generate a database connection, using the PDO connector 34 | // @see http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/ 35 | $this->db = new PDO(DB_TYPE . ':host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASS, $options); 36 | } 37 | 38 | /** 39 | * Load the model with the given name. 40 | * loadModel("SongModel") would include models/songmodel.php and create the object in the controller, like this: 41 | * $songs_model = $this->loadModel('SongsModel'); 42 | * Note that the model class name is written in "CamelCase", the model's filename is the same in lowercase letters 43 | * @param string $model_name The name of the model 44 | * @return object model 45 | */ 46 | public function loadModel($model_name) 47 | { 48 | require 'application/models/' . strtolower($model_name) . '.php'; 49 | // return new model (and pass the database connection to the model) 50 | return new $model_name($this->db); 51 | } 52 | 53 | public function render($view, $data_array = array()) 54 | { 55 | // load Twig, the template engine 56 | // @see http://twig.sensiolabs.org 57 | $twig_loader = new Twig_Loader_Filesystem(PATH_VIEWS); 58 | $twig = new Twig_Environment($twig_loader); 59 | 60 | // render a view while passing the to-be-rendered data 61 | echo $twig->render($view . PATH_VIEW_FILE_TYPE, $data_array); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /application/models/songsmodel.php: -------------------------------------------------------------------------------- 1 | db = $db; 12 | } catch (PDOException $e) { 13 | exit('Database connection could not be established.'); 14 | } 15 | } 16 | 17 | /** 18 | * Get all songs from database 19 | */ 20 | public function getAllSongs() 21 | { 22 | $sql = "SELECT id, artist, track, link FROM song"; 23 | $query = $this->db->prepare($sql); 24 | $query->execute(); 25 | 26 | // fetchAll() is the PDO method that gets all result rows, here in object-style because we defined this in 27 | // libs/controller.php! If you prefer to get an associative array as the result, then do 28 | // $query->fetchAll(PDO::FETCH_ASSOC); or change libs/controller.php's PDO options to 29 | // $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ... 30 | return $query->fetchAll(); 31 | } 32 | 33 | /** 34 | * Add a song to database 35 | * @param string $artist Artist 36 | * @param string $track Track 37 | * @param string $link Link 38 | */ 39 | public function addSong($artist, $track, $link) 40 | { 41 | // clean the input from javascript code for example 42 | $artist = strip_tags($artist); 43 | $track = strip_tags($track); 44 | $link = strip_tags($link); 45 | 46 | $sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)"; 47 | $query = $this->db->prepare($sql); 48 | $query->execute(array(':artist' => $artist, ':track' => $track, ':link' => $link)); 49 | } 50 | 51 | /** 52 | * Delete a song in the database 53 | * Please note: this is just an example! In a real application you would not simply let everybody 54 | * add/update/delete stuff! 55 | * @param int $song_id Id of song 56 | */ 57 | public function deleteSong($song_id) 58 | { 59 | $sql = "DELETE FROM song WHERE id = :song_id"; 60 | $query = $this->db->prepare($sql); 61 | $query->execute(array(':song_id' => $song_id)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /application/models/statsmodel.php: -------------------------------------------------------------------------------- 1 | db = $db; 12 | } catch (PDOException $e) { 13 | exit('Database connection could not be established.'); 14 | } 15 | } 16 | 17 | /** 18 | * Get simple "stats". This is just a simple demo to show 19 | * how to use more than one model in a controller (see application/controller/songs.php for more) 20 | */ 21 | public function getAmountOfSongs() 22 | { 23 | $sql = "SELECT COUNT(id) AS amount_of_songs FROM song"; 24 | $query = $this->db->prepare($sql); 25 | $query->execute(); 26 | 27 | // fetchAll() is the PDO method that gets all result rows 28 | return $query->fetch()->amount_of_songs; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /application/views/_templates/base.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PHP MVC skeleton 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |
21 | Everything in this box is loaded from application/views/_templates/header.php ! 22 |
23 | The green line is added via JavaScript (to show how to integrate JavaScript). 24 |
25 |

The header (used on all pages)

26 | 27 |

Demo image, to show usage of public/img folder

28 |
29 | 30 |
31 | 32 |

Demo Navigation

33 | 43 | 44 | 45 |

Demo JavaScript

46 |
47 |
48 |
49 | 50 | 51 | {% block content %} {% endblock %} 52 | 53 | 54 | -------------------------------------------------------------------------------- /application/views/home/example_one.twig: -------------------------------------------------------------------------------- 1 | {% extends "_templates/base.twig" %} 2 | {% block content %} 3 |
4 |

You are in the View: application/views/home/example_one.php (everything in the box comes from this file)

5 |

In a real application this could be a normal page.

6 |
7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /application/views/home/example_two.twig: -------------------------------------------------------------------------------- 1 | {% extends "_templates/base.twig" %} 2 | {% block content %} 3 |
4 |

You are in the View: application/views/home/example_two.php (everything in the box comes from this file)

5 |

In a real application this could be a normal page.

6 |
7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /application/views/home/index.twig: -------------------------------------------------------------------------------- 1 | {% extends "_templates/base.twig" %} 2 | {% block content %} 3 |
4 |

You are in the View: application/views/home/index.php (everything in the box comes from this file)

5 |

In a real application this could be the homepage.

6 |
7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /application/views/songs/index.twig: -------------------------------------------------------------------------------- 1 | {% extends "_templates/base.twig" %} 2 | {% block content %} 3 |
4 |

You are in the View: application/views/song/index.php (everything in this box comes from that file)

5 | 6 |
7 |

Add a song

8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 |
20 |

Amount of songs (data from second model)

21 |
22 | {{ amount_of_songs }} 23 |
24 |

List of songs (data from first model)

25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {% for song in songs %} 37 | 38 | 39 | 40 | 41 | 46 | 49 | 50 | {% endfor %} 51 | 52 |
IdArtistTrackLinkDELETE
{{ song.id }}{{ song.artist }}{{ song.track }} 42 | {% if song.link %} 43 | {{ song.link }} 44 | {% endif %} 45 | 47 | x 48 |
53 |
54 |
55 | {% endblock %} -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "panique/php-mvc-advanced", 3 | "type": "project", 4 | "description": "A simple PHP MVC boilerplate - Advanced version", 5 | "keywords": ["mvc", "skeleton", "boilerplate", "naked", "barebone", "application"], 6 | "homepage": "https://github.com/panique/php-mvc-advanced", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Panique", 11 | "homepage": "http://careers.stackoverflow.com/panique", 12 | "role": "Developer" 13 | } 14 | ], 15 | "support": { 16 | "issues": "https://github.com/panique/php-mvc-advanced/issues", 17 | "source": "https://github.com/panique/php-mvc-advanced" 18 | }, 19 | "require": { 20 | "php": ">=5.3.0", 21 | "twig/twig": "1.*" 22 | }, 23 | "require-dev": { 24 | "panique/laravel-sass": "dev-master" 25 | } 26 | } -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 |