├── .htaccess ├── CHANGELOG.md ├── README.md ├── _tutorial ├── donate-with-flattr.png ├── donate-with-paypal.png ├── support-a2hosting.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 │ ├── footer.php │ └── header.php │ ├── home │ ├── example_one.php │ ├── example_two.php │ └── index.php │ └── songs │ └── index.php ├── composer.json ├── index.php └── public ├── css └── style.css ├── img └── demo-image.png └── js └── application.js /.htaccess: -------------------------------------------------------------------------------- 1 | # Necessary to prevent problems when using a controller named "index" and having 2 | # a root index.php file. 3 | # For more information, see 4 | # http://httpd.apache.org/docs/2.2/content-negotiation.html 5 | Options -MultiViews 6 | 7 | # In some cases, setting RewriteEngine On results in 403 Forbidden unless you 8 | # uncomment the following line. 9 | # Options +FollowSymLinks 10 | 11 | # Activates URL rewriting, using the rewriting rules defined later in this file. 12 | # For example, allows Apache to rewrite a URL like 13 | # http://myproject.com/controller/action/1/2/3 to go to the appropriate page. 14 | RewriteEngine On 15 | 16 | # Prevents others from looking directly into the /public/ folder. 17 | Options -Indexes 18 | 19 | # If you're installing php-mvc in a subfolder, then specify the php-mvc 20 | # root folder here, like /mysubfolder/ 21 | # If your app is in the root of your web folder, then leave this line 22 | # commented out. 23 | # RewriteBase /php-mvc/ 24 | 25 | # If the following conditions are true, then rewrite the URL. 26 | 27 | # If the requested filename is not a directory, 28 | RewriteCond %{REQUEST_FILENAME} !-d 29 | 30 | # and if the requested filename is not a regular file that exists, 31 | RewriteCond %{REQUEST_FILENAME} !-f 32 | 33 | # and if the requested filename is not a symbolic link, 34 | RewriteCond %{REQUEST_FILENAME} !-l 35 | 36 | # then rewrite the URL in the following way: 37 | # Take the whole request filename and provide it as the value of a 38 | # "url" query parameter to index.php. Append any query string from 39 | # the original URL as further query parameters (QSA), and stop 40 | # processing this .htaccess file (L). 41 | RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] 42 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGE LOG 2 | ========== 3 | 4 | ** June 2014 ** 5 | - [digitaltoast] removed X-UA-Compatible meta tag from header (as it's not needed anymore these days) 6 | - [digitaltoast] removed protocol in jQuery URL (modern way to load external files, making it independent to protocol change) 7 | - [digitaltoast] downgraded jQuery from 2.1 to 1.11 to avoid problems when working with IE7/8 (jQuery 2 dropped IE7/8 support) 8 | 9 | ** April 2014 ** 10 | - updated jQuery link to 2.1 11 | - more than 3 parameters (arguments to be concrete) are possible 12 | - cleaner way of parameter handling 13 | - smaller cleanings and improvements 14 | - Apache 2.4 install information 15 | 16 | **January 4th 2014** 17 | - fixed .htaccess issue when there's a controller named "index" and a base index.php (which collide) 18 | 19 | **December 29th 2013** 20 | - [grrnikos] fixed case-sensitive model file loading 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-MVC 2 | 3 | An extremely simple and easy to understand MVC skeleton application, 4 | reduced to the max. Everything is **as simple as possible**, **as 5 | manually written as possible**, and as readable as possible. This 6 | project is--by intention--NOT a full framework; it's a bare-bones 7 | structure, written in purely native PHP! The php-mvc skeleton tries to 8 | be the extremely slimmed down opposite of big frameworks like Zend2, 9 | Symfony, or Laravel. 10 | 11 | [![Donate by server affiliate sale](_tutorial/support-a2hosting.png)](https://affiliates.a2hosting.com/idevaffiliate.php?id=4471&url=579) 12 | 13 | ## Basic and Advanced versions 14 | 15 | This is the basic version. There's an [advanced 16 | version](https://github.com/panique/php-mvc-advanced) in development 17 | that has additional features, currently Twig and SASS. More to come! 18 | Have a look. 19 | 20 | ## Why does this project exist ? 21 | 22 | One of the biggest questions in the PHP world is "How do I build an 23 | application?" It's hard to find a good base, a good file structure, 24 | and useful information; but at the same time, there are masses of 25 | frameworks that might be really good, but really hard to understand, 26 | hard to use, and extremely complex. This project tries to be a kind of 27 | naked bare-bones skeleton for quick application building, especially 28 | 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 use of PDO. 36 | - Promote the use of external libraries via Composer. 37 | - Promote development with maximum error reporting. 38 | - Promote commenting in code. 39 | - Promote the use of object-oriented programming. 40 | - Use only native PHP code, so people don't have to learn a framework. 41 | 42 | ## Support forum 43 | 44 | If you are stuck even AFTER reading and following the install 45 | tutorials and the quick-manual, then feel free to ask in the [official 46 | forum](http://forum.php-mvc.net/). Note that this forum is fresh and 47 | new; more content will come over time. 48 | 49 | ## Keep on track with development by following ... 50 | 51 | ... [on 52 | Facebook](https://www.facebook.com/pages/The-PHP-MVC-Project/1488883564703966), 53 | or check this GitHub repo. 54 | 55 | ## Installation 56 | 57 | ### On Windows 7 (with EasyPHP) 58 | 59 | There's a tutorial on [How to install php-mvc on Windows 7, 8 and 60 | 8.1](http://www.dev-metal.com/install-php-mvc-windows-7/). 61 | 62 | ### On Ubuntu 12.04 LTS 63 | 64 | Ubuntu 12.04 LTS is the most common mainstream server operating 65 | system. Unless you know what you're doing and have a very good reason 66 | to use something else: Use this version! 67 | 68 | First, copy this repo into a publicly accessible folder on your 69 | server. Common techniques are a) downloading and extracting the .zip / 70 | .tgz by hand, or b) cloning the repo with git (into var/www). 71 | 72 | ``` 73 | git clone https://github.com/panique/php-mvc.git /var/www 74 | ``` 75 | 76 | or c) getting the repo via Composer (copying into var/www) 77 | 78 | ``` 79 | composer create-project panique/php-mvc /var/www dev-master 80 | ``` 81 | 82 | 1. Install mod_rewrite, for example by following this guideline: [How 83 | to install mod_rewrite in 84 | Ubuntu](http://www.dev-metal.com/enable-mod_rewrite-ubuntu-12-04-lts/). 85 | 86 | 2. Run the SQL statements in the *application/_install* folder (after 87 | modifying them to use the name of your database if necessary). 88 | 89 | 3. Change the .htaccess file from 90 | ``` 91 | RewriteBase /php-mvc/ 92 | ``` 93 | to where you put this project, relative to the web root folder 94 | (usually /var/www). So when you put this project into the web root, 95 | like directly in /var/www, then the line should look like the 96 | following, or can be commented out: 97 | ``` 98 | RewriteBase / 99 | ``` 100 | If you have put the project into a subfolder, then put the name of the 101 | subfolder here: 102 | ``` 103 | RewriteBase /subfolder/ 104 | ``` 105 | 106 | 4. Edit the *application/config/config.php* file to change this line: 107 | ```php 108 | define('URL', 'http://127.0.0.1/php-mvc/'); 109 | ``` 110 | to where your project is--your real domain URL, your IP address, or 111 | 127.0.0.1 when developing locally. Don't forget the trailing slash! 112 | And if you're installing in a subfolder instead of your web root, then 113 | include the subfolder in this URL. 114 | 115 | 5. Edit the *application/config/config.php* file to change these lines: 116 | ```php 117 | define('DB_TYPE', 'mysql'); 118 | define('DB_HOST', '127.0.0.1'); 119 | define('DB_NAME', 'php-mvc'); 120 | define('DB_USER', 'root'); 121 | define('DB_PASS', 'mysql'); 122 | ``` 123 | to your database credentials. If you don't have an empty database, 124 | create one. Don't change the type `mysql` unless you know what you are 125 | doing. 126 | 127 | ### On Ubuntu 14.04 LTS 128 | 129 | Ubuntu 14.04 LTS comes with a newer version of Apache (2.4) that has 130 | slightly different configs and config syntax, and different filenames. 131 | Here's a tutorial on [how to enable mod_rewrite in Ubuntu 14.04 132 | LTS](http://www.dev-metal.com/enable-mod_rewrite-ubuntu-14-04-lts/). 133 | To update the application itself for Ubuntu 14.04 LTS, change 134 | `application/.htaccess` to `Require all denied`. For more details, see 135 | the [official Apache 136 | docs](http://httpd.apache.org/docs/2.4/upgrading.html). Thanks to 137 | *adamholte* for the info! 138 | 139 | ## A quickstart tutorial 140 | 141 | You can also find these tutorial pictures in the *_tutorial* folder. 142 | 143 | ![php-mvc introduction tutorial - page 1](_tutorial/tutorial-part-01.png) 144 | ![php-mvc introduction tutorial - page 2](_tutorial/tutorial-part-02.png) 145 | ![php-mvc introduction tutorial - page 3](_tutorial/tutorial-part-03.png) 146 | ![php-mvc introduction tutorial - page 4](_tutorial/tutorial-part-04.png) 147 | ![php-mvc introduction tutorial - page 5](_tutorial/tutorial-part-05.png) 148 | 149 | ## You like what you see ? 150 | 151 | Then please also have a look at... 152 | 153 | #### My php-login project 154 | 155 | A collection of four similar login scripts for PHP, from a 156 | super-simple one-file script using SQLite to a highly professional MVC 157 | frameworks solution. All scripts use the most advanced hashing 158 | algorithms possible in PHP, exactly like the PHP core developers want 159 | you to use them. 160 | 161 | https://github.com/panique/php-login (full MVC framework) 162 | 163 | https://github.com/panique/php-login-minimal (minimal) 164 | 165 | https://github.com/panique/php-login-advanced (advanced) 166 | 167 | https://github.com/panique/php-login-one-file (one-file) 168 | 169 | #### My PHP and frontend blog 170 | 171 | Lots of non-boring development stuff and tutorials there. 172 | 173 | http://www.dev-metal.com 174 | 175 | ## Useful information 176 | 177 | 1. SQLite does not have a rowCount() method (!). Keep that in mind in 178 | case you use SQLite. 179 | 180 | 2. Don't use the same name for class and method, as this might trigger 181 | an (unintended) *__construct* of the class. This is really weird 182 | behaviour, but documented on php.net: [Constructors and 183 | Destructors](http://php.net/manual/en/language.oop5.decon.php). 184 | 185 | ## Add external libraries via Composer 186 | 187 | To add external libraries/tools/whatever into your project in an 188 | extremely clean way, simply add a line with the repo name and version 189 | to the composer.json! If you want to get into Composer, take a look at 190 | these tutorials: 191 | [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/) 192 | and [Getting started with Composer](http://www.dev-metal.com/getting-started-composer/). 193 | 194 | ## License 195 | 196 | This project is licensed under the MIT License. 197 | This means you can use and modify it for free in private or commercial 198 | projects. 199 | 200 | ## Contribute 201 | 202 | Please commit into the **develop branch** (which holds the 203 | in-development version), not into master branch (which holds the 204 | tested and stable version). 205 | 206 | ## Support / Donate 207 | 208 | If you think this script is useful and saves you a lot of work, then 209 | think about supporting the project: 210 | 211 | A. 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). 212 | B. Rent your next server at [A2 Hosting](http://www.a2hosting.com/4471.html) or [DigitalOcean](https://www.digitalocean.com/?refcode=40d978532a20). 213 | C. Contribute code to this project on GitHub. 214 | D. Spread the word: Tell others about this project. 215 | 216 | ## Linked music tracks in the demo application 217 | 218 | The linked tracks in this naked application are just some of my 219 | personal favourites of the last few months. I think it's always a good 220 | idea to fill boring nerd-code stuff with quality culture. 221 | -------------------------------------------------------------------------------- /_tutorial/donate-with-flattr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_tutorial/donate-with-flattr.png -------------------------------------------------------------------------------- /_tutorial/donate-with-paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_tutorial/donate-with-paypal.png -------------------------------------------------------------------------------- /_tutorial/support-a2hosting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_tutorial/support-a2hosting.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_tutorial/tutorial-part-01.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_tutorial/tutorial-part-02.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_tutorial/tutorial-part-03.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_tutorial/tutorial-part-04.png -------------------------------------------------------------------------------- /_tutorial/tutorial-part-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elysdir/php-mvc/f0ef2d9604e2ce8e022849b1de52f6a99dcdca62/_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 | loadModel('SongsModel'); 26 | $songs = $songs_model->getAllSongs(); 27 | 28 | // load another model, perform an action, pass the returned data to a variable 29 | // NOTE: please write the name of the model "LikeThis" 30 | $stats_model = $this->loadModel('StatsModel'); 31 | $amount_of_songs = $stats_model->getAmountOfSongs(); 32 | 33 | // load views. within the views we can echo out $songs and $amount_of_songs easily 34 | require 'application/views/_templates/header.php'; 35 | require 'application/views/songs/index.php'; 36 | require 'application/views/_templates/footer.php'; 37 | } 38 | 39 | /** 40 | * ACTION: addSong 41 | * This method handles what happens when you move to http://yourproject/songs/addsong 42 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "add a song" form on songs/index 43 | * directs the user after the form submit. This method handles all the POST data from the form and then redirects 44 | * the user back to songs/index via the last line: header(...) 45 | * This is an example of how to handle a POST request. 46 | */ 47 | public function addSong() 48 | { 49 | 50 | // if we have POST data to create a new song entry 51 | if (isset($_POST["submit_add_song"])) { 52 | // load model, perform an action on the model 53 | $songs_model = $this->loadModel('SongsModel'); 54 | $songs_model->addSong($_POST["artist"], $_POST["track"], $_POST["link"]); 55 | } 56 | 57 | // where to go after song has been added 58 | header('location: ' . URL . 'songs/index'); 59 | } 60 | 61 | /** 62 | * ACTION: deleteSong 63 | * This method handles what happens when you move to http://yourproject/songs/deletesong 64 | * IMPORTANT: This is not a normal page, it's an ACTION. This is where the "delete a song" button on songs/index 65 | * directs the user after the click. This method handles all the data from the GET request (in the URL!) and then 66 | * redirects the user back to songs/index via the last line: header(...) 67 | * This is an example of how to handle a GET request. 68 | * @param int $song_id Id of the to-delete song 69 | */ 70 | public function deleteSong($song_id) 71 | { 72 | 73 | // if we have an id of a song that should be deleted 74 | if (isset($song_id)) { 75 | // load model, perform an action on the model 76 | $songs_model = $this->loadModel('SongsModel'); 77 | $songs_model->deleteSong($song_id); 78 | } 79 | 80 | // where to go after song has been deleted 81 | header('location: ' . URL . 'songs/index'); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /application/libs/application.php: -------------------------------------------------------------------------------- 1 | splitUrl(); 22 | 23 | // check for controller: does such a controller exist ? 24 | if (file_exists('./application/controller/' . $this->url_controller . '.php')) { 25 | 26 | // if so, then load this file and create this controller 27 | // example: if controller would be "car", then this line would translate into: $this->car = new car(); 28 | require './application/controller/' . $this->url_controller . '.php'; 29 | $this->url_controller = new $this->url_controller(); 30 | 31 | // check for method: does such a method exist in the controller ? 32 | if (method_exists($this->url_controller, $this->url_action)) { 33 | 34 | if(!empty($this->url_params)) { 35 | // Call the method and pass arguments to it 36 | call_user_func_array(array($this->url_controller, $this->url_action), $this->url_params); 37 | } else { 38 | // If no parameters are given, just call the method without parameters, like $this->home->method(); 39 | $this->url_controller->{$this->url_action}(); 40 | } 41 | 42 | } else { 43 | // default/fallback: call the index() method of a selected controller 44 | $this->url_controller->index(); 45 | } 46 | } else { 47 | // invalid URL, so simply show home/index 48 | require './application/controller/home.php'; 49 | $home = new Home(); 50 | $home->index(); 51 | } 52 | } 53 | 54 | /** 55 | * Get and split the URL 56 | */ 57 | private function splitUrl() 58 | { 59 | if (isset($_GET['url'])) { 60 | 61 | // split URL 62 | $url = rtrim($_GET['url'], '/'); 63 | $url = filter_var($url, FILTER_SANITIZE_URL); 64 | $url = explode('/', $url); 65 | 66 | // Put URL parts into according properties 67 | // By the way, the syntax here is just a short form of if/else, called "Ternary Operators" 68 | // @see http://davidwalsh.name/php-shorthand-if-else-ternary-operators 69 | $this->url_controller = isset($url[0]) ? $url[0] : null; 70 | $this->url_action = isset($url[1]) ? $url[1] : null; 71 | 72 | // Remove controller and action from the split URL 73 | unset($url[0], $url[1]); 74 | 75 | // Rebase array keys and store the URL params 76 | $this->url_params = array_values($url); 77 | 78 | // for debugging. uncomment this if you have problems with the URL 79 | //echo 'Controller: ' . $this->url_controller . '
'; 80 | //echo 'Action: ' . $this->url_action . '
'; 81 | //echo 'Parameters: ' . print_r($this->url_params, true) . '
'; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /application/models/songsmodel.php: -------------------------------------------------------------------------------- 1 | db = $db; 13 | } catch (PDOException $e) { 14 | exit('Database connection could not be established.'); 15 | } 16 | } 17 | 18 | /** 19 | * Get all songs from database 20 | */ 21 | public function getAllSongs() 22 | { 23 | $sql = "SELECT id, artist, track, link FROM song"; 24 | $query = $this->db->prepare($sql); 25 | $query->execute(); 26 | 27 | // fetchAll() is the PDO method that gets all result rows, here in object-style because we defined this in 28 | // libs/controller.php! If you prefer to get an associative array as the result, then do 29 | // $query->fetchAll(PDO::FETCH_ASSOC); or change libs/controller.php's PDO options to 30 | // $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ... 31 | return $query->fetchAll(); 32 | } 33 | 34 | /** 35 | * Add a song to database 36 | * @param string $artist Artist 37 | * @param string $track Track 38 | * @param string $link Link 39 | */ 40 | public function addSong($artist, $track, $link) 41 | { 42 | // clean the input from javascript code for example 43 | $artist = strip_tags($artist); 44 | $track = strip_tags($track); 45 | $link = strip_tags($link); 46 | 47 | $sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)"; 48 | $query = $this->db->prepare($sql); 49 | $query->execute(array(':artist' => $artist, ':track' => $track, ':link' => $link)); 50 | } 51 | 52 | /** 53 | * Delete a song in the database 54 | * Please note: this is just an example! In a real application you would not simply let everybody 55 | * add/update/delete stuff! 56 | * @param int $song_id Id of song 57 | */ 58 | public function deleteSong($song_id) 59 | { 60 | $sql = "DELETE FROM song WHERE id = :song_id"; 61 | $query = $this->db->prepare($sql); 62 | $query->execute(array(':song_id' => $song_id)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /application/models/statsmodel.php: -------------------------------------------------------------------------------- 1 | db = $db; 13 | } catch (PDOException $e) { 14 | exit('Database connection could not be established.'); 15 | } 16 | } 17 | 18 | /** 19 | * Get simple "stats". This is just a simple demo to show 20 | * how to use more than one model in a controller (see application/controller/songs.php for more) 21 | */ 22 | public function getAmountOfSongs() 23 | { 24 | $sql = "SELECT COUNT(id) AS amount_of_songs FROM song"; 25 | $query = $this->db->prepare($sql); 26 | $query->execute(); 27 | 28 | // fetchAll() is the PDO method that gets all result rows 29 | return $query->fetch()->amount_of_songs; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /application/views/_templates/footer.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /application/views/_templates/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PHP MVC skeleton 6 | 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 | Demo image 30 |
31 | 32 |

Demo Navigation

33 | 43 | 44 |

Demo JavaScript

45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /application/views/home/example_one.php: -------------------------------------------------------------------------------- 1 |
2 |

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

3 |

In a real application this could be a normal page.

4 |
5 | -------------------------------------------------------------------------------- /application/views/home/example_two.php: -------------------------------------------------------------------------------- 1 |
2 |

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

3 |

In a real application this could be a normal page.

4 |
5 | -------------------------------------------------------------------------------- /application/views/home/index.php: -------------------------------------------------------------------------------- 1 |
2 |

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

3 |

In a real application this could be the homepage.

4 |
5 | -------------------------------------------------------------------------------- /application/views/songs/index.php: -------------------------------------------------------------------------------- 1 |
2 |

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

3 | 4 |
5 |

Add a song

6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |

Amount of songs (data from second model)

19 |
20 | 21 |
22 |

List of songs (data from first model)

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 |
IdArtistTrackLinkDELETE
id)) echo $song->id; ?>artist)) echo $song->artist; ?>track)) echo $song->track; ?> 40 | link)) { ?> 41 | link; ?> 42 | 43 | x
49 |
50 |
51 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "panique/php-mvc", 3 | "type": "project", 4 | "description": "A simple PHP MVC boilerplate", 5 | "keywords": ["mvc", "skeleton", "boilerplate", "naked", "barebone", "application"], 6 | "homepage": "https://github.com/panique/php-mvc", 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/issues", 17 | "source": "https://github.com/panique/php-mvc" 18 | }, 19 | "require": { 20 | "php": ">=5.3.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 |