├── .htaccess ├── README.md ├── application ├── bootstrap.php ├── controllers │ ├── controller_404.php │ ├── controller_admin.php │ ├── controller_contacts.php │ ├── controller_login.php │ ├── controller_main.php │ ├── controller_portfolio.php │ └── controller_services.php ├── core │ ├── controller.php │ ├── model.php │ ├── route.php │ └── view.php ├── models │ └── model_portfolio.php └── views │ ├── 404_view.php │ ├── admin_view.php │ ├── contacts_view.php │ ├── login_view.php │ ├── main_view.php │ ├── portfolio_view.php │ ├── services_view.php │ └── template_view.php ├── css └── style.css ├── images ├── 00bd.png ├── 404.png ├── cap.jpg ├── office-full.jpg └── office-small.jpg ├── index.php └── js └── jquery-1.6.2.js /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteCond %{REQUEST_FILENAME} !-d 4 | RewriteRule .* index.php [L] 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 

2 | Исходные коды проекта, описанного в статье: "Реализация MVC паттерна на примере создания сайта-визитки на PHP". 3 |
4 | © vitalyswipe 5 |

6 | -------------------------------------------------------------------------------- /application/bootstrap.php: -------------------------------------------------------------------------------- 1 | аутентификацию 11 | > кеширование 12 | > работу с формами 13 | > абстракции для доступа к данным 14 | > ORM 15 | > Unit тестирование 16 | > Benchmarking 17 | > Работу с изображениями 18 | > Backup 19 | > и др. 20 | */ 21 | 22 | require_once 'core/route.php'; 23 | Route::start(); // запускаем маршрутизатор 24 | -------------------------------------------------------------------------------- /application/controllers/controller_404.php: -------------------------------------------------------------------------------- 1 | view->generate('404_view.php', 'template_view.php'); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /application/controllers/controller_admin.php: -------------------------------------------------------------------------------- 1 | view->generate('admin_view.php', 'template_view.php'); 18 | } 19 | else 20 | { 21 | session_destroy(); 22 | Route::ErrorPage404(); 23 | } 24 | 25 | } 26 | 27 | // Действие для разлогинивания администратора 28 | function action_logout() 29 | { 30 | session_start(); 31 | session_destroy(); 32 | header('Location:/'); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /application/controllers/controller_contacts.php: -------------------------------------------------------------------------------- 1 | view->generate('contacts_view.php', 'template_view.php'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /application/controllers/controller_login.php: -------------------------------------------------------------------------------- 1 | view->generate('login_view.php', 'template_view.php', $data); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /application/controllers/controller_main.php: -------------------------------------------------------------------------------- 1 | view->generate('main_view.php', 'template_view.php'); 9 | } 10 | } -------------------------------------------------------------------------------- /application/controllers/controller_portfolio.php: -------------------------------------------------------------------------------- 1 | model = new Model_Portfolio(); 9 | $this->view = new View(); 10 | } 11 | 12 | function action_index() 13 | { 14 | $data = $this->model->get_data(); 15 | $this->view->generate('portfolio_view.php', 'template_view.php', $data); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /application/controllers/controller_services.php: -------------------------------------------------------------------------------- 1 | view->generate('services_view.php', 'template_view.php'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /application/core/controller.php: -------------------------------------------------------------------------------- 1 | view = new View(); 11 | } 12 | 13 | // действие (action), вызываемое по умолчанию 14 | function action_index() 15 | { 16 | // todo 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /application/core/model.php: -------------------------------------------------------------------------------- 1 | методы нативных библиотек pgsql или mysql; 9 | > методы библиотек, реализующих абстракицю данных. Например, методы библиотеки PEAR MDB2; 10 | > методы ORM; 11 | > методы для работы с NoSQL; 12 | > и др. 13 | */ 14 | 15 | // метод выборки данных 16 | public function get_data() 17 | { 18 | // todo 19 | } 20 | } -------------------------------------------------------------------------------- /application/core/route.php: -------------------------------------------------------------------------------- 1 | цепляет классы контроллеров и моделей; 6 | > создает экземпляры контролеров страниц и вызывает действия этих контроллеров. 7 | */ 8 | class Route 9 | { 10 | 11 | static function start() 12 | { 13 | // контроллер и действие по умолчанию 14 | $controller_name = 'Main'; 15 | $action_name = 'index'; 16 | 17 | $routes = explode('/', $_SERVER['REQUEST_URI']); 18 | 19 | // получаем имя контроллера 20 | if ( !empty($routes[1]) ) 21 | { 22 | $controller_name = $routes[1]; 23 | } 24 | 25 | // получаем имя экшена 26 | if ( !empty($routes[2]) ) 27 | { 28 | $action_name = $routes[2]; 29 | } 30 | 31 | // добавляем префиксы 32 | $model_name = 'Model_'.$controller_name; 33 | $controller_name = 'Controller_'.$controller_name; 34 | $action_name = 'action_'.$action_name; 35 | 36 | /* 37 | echo "Model: $model_name
"; 38 | echo "Controller: $controller_name
"; 39 | echo "Action: $action_name
"; 40 | */ 41 | 42 | // подцепляем файл с классом модели (файла модели может и не быть) 43 | 44 | $model_file = strtolower($model_name).'.php'; 45 | $model_path = "application/models/".$model_file; 46 | if(file_exists($model_path)) 47 | { 48 | include "application/models/".$model_file; 49 | } 50 | 51 | // подцепляем файл с классом контроллера 52 | $controller_file = strtolower($controller_name).'.php'; 53 | $controller_path = "application/controllers/".$controller_file; 54 | if(file_exists($controller_path)) 55 | { 56 | include "application/controllers/".$controller_file; 57 | } 58 | else 59 | { 60 | /* 61 | правильно было бы кинуть здесь исключение, 62 | но для упрощения сразу сделаем редирект на страницу 404 63 | */ 64 | Route::ErrorPage404(); 65 | } 66 | 67 | // создаем контроллер 68 | $controller = new $controller_name; 69 | $action = $action_name; 70 | 71 | if(method_exists($controller, $action)) 72 | { 73 | // вызываем действие контроллера 74 | $controller->$action(); 75 | } 76 | else 77 | { 78 | // здесь также разумнее было бы кинуть исключение 79 | Route::ErrorPage404(); 80 | } 81 | 82 | } 83 | 84 | function ErrorPage404() 85 | { 86 | $host = 'http://'.$_SERVER['HTTP_HOST'].'/'; 87 | header('HTTP/1.1 404 Not Found'); 88 | header("Status: 404 Not Found"); 89 | header('Location:'.$host.'404'); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /application/core/view.php: -------------------------------------------------------------------------------- 1 | '2012', 15 | 'Site' => 'http://DunkelBeer.ru', 16 | 'Description' => 'Промо-сайт темного пива Dunkel от немецкого производителя Löwenbraü выпускаемого в России пивоваренной компанией "CАН ИнБев".' 17 | ), 18 | 19 | array( 20 | 'Year' => '2012', 21 | 'Site' => 'http://ZopoMobile.ru', 22 | 'Description' => 'Русскоязычный каталог китайских телефонов компании Zopo на базе Android OS и аксессуаров к ним.' 23 | ), 24 | 25 | array( 26 | 'Year' => '2012', 27 | 'Site' => 'http://GeekWear.ru', 28 | 'Description' => 'Интернет-магазин брендовой одежды для гиков.' 29 | ), 30 | 31 | array( 32 | 'Year' => '2011', 33 | 'Site' => 'http://РоналВарвар.рф', 34 | 'Description' => 'Промо-сайт мультфильма "Ронал-варвар" от норвежских режиссеров. Мультфильм о самом нетипичном варваре на Земле, переполненный интересными приключениями и забавными ситуациями.' 35 | ), 36 | 37 | array( 38 | 'Year' => '2011', 39 | 'Site' => 'http://TompsonTatoo.ru', 40 | 'Description' => 'Персональный сайт-блог художника-татуировщика Алексея Томпсона из Санкт-Петербурга.' 41 | ), 42 | 43 | array( 44 | 'Year' => '2011', 45 | 'Site' => 'http://DaftState.ru', 46 | 'Description' => 'Страничка музыкальных и сануд продюсеров из команды "DaftState", работающих в стилях BreakBeat и BigBeat.' 47 | ), 48 | 49 | array( 50 | 'Year' => '2011', 51 | 'Site' => 'http://TiltPeople.ru', 52 | 'Description' => 'Сайт сообщества фотографов в стиле Tilt Shif.' 53 | ), 54 | 55 | array( 56 | 'Year' => '2011', 57 | 'Site' => 'http://AbsurdGames.ru', 58 | 'Description' => 'Страничка российской команды разработчиков независимых игр с необычной физикой и сюрреалистической графикой.' 59 | ), 60 | 61 | ); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /application/views/404_view.php: -------------------------------------------------------------------------------- 1 |

404

2 |

3 | 4 |

5 | -------------------------------------------------------------------------------- /application/views/admin_view.php: -------------------------------------------------------------------------------- 1 |

Панель администрирования

2 |

3 | Админка... 4 | 11 |

12 | -------------------------------------------------------------------------------- /application/views/contacts_view.php: -------------------------------------------------------------------------------- 1 |

Контакты

2 |

3 | icq: 199199538
4 | skypeid: vitalyswipe
5 | email: vitalyswipe@gmail.com
6 |
7 | 10 |

11 | -------------------------------------------------------------------------------- /application/views/login_view.php: -------------------------------------------------------------------------------- 1 |

Страница авторизации

2 |

3 |

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 |
Авторизация
Логин
Пароль
17 |
20 |
21 |

22 | 23 | 24 | 25 |

Авторизация прошла успешно.

26 | 27 |

Логин и/или пароль введены неверно.

28 | 29 | -------------------------------------------------------------------------------- /application/views/main_view.php: -------------------------------------------------------------------------------- 1 |

Добро пожаловать!

2 |

3 | 4 | ОЛОЛОША TEAM - команда первоклассных специалистов в области разработки веб-сайтов с многолетним опытом коллекционирования мексиканских масок, бронзовых и каменных статуй из Индии и Цейлона, барельефов и изваяний, созданных мастерами Экваториальной Африки пять-шесть веков назад. Сотрудники компании надевают костюм и галстук при посещении некоторых фешенебельных ресторанов, сосредоточенных в районе Центральной площади и железнодорожного вокзала. Полынно-кустарниковая растительность здесь параллельна. Пересечения улиц сплетаются в урбанистический кедровый пейзаж, при этом разрешен провоз 3 бутылок крепких спиртных напитков, 2 бутылок вина; 1 литр духов в откупоренных флаконах, 2 литра одеколона в откупоренных флаконах. Когда из офиса с шумом выбегают мужчины в костюмах демонов и смешиваются с толпой, царящая в нашей студии атмосфера напоминает очаг многовекового орошаемого земледелия, и не надо забывать, что время здесь отстает от московского на 2 часа. Из первых блюд распространены супы-пюре и бульоны, но подают их редко, 5 | Для гостей также открываются погреба Прибалатонских винодельческих хозяйств, известных отличными сортами вин "Олазрислинг" и "Сюркебарат". 6 | В ресторане стоимость обслуживания (15%) включена в счет; в баре и кафе - 10-15% счета только за услуги официанта; в такси - чаевые включены в стоимость проезда, тем не менее портер неравномерен. 7 | В пределах личных потребностей сотрудники компании точно отражают широкий кристаллический фундамент. Помимо трендовых сертификатов, также имеются справки о прививках против бешенства и результаты анализа на бешенство через 120 дней и за 30 дней до начала работы над вашим проектом. Большинство сотрудников компании (около 5%) очень дружелюбны, приветливы и гостеприимны. При этом королевские полномочия находятся в руках бамбукового медведя панды. Это и есть всемирно известный центр огранки алмазов и торговли бриллиантами - ОЛОЛОША TEAM. 8 |

-------------------------------------------------------------------------------- /application/views/portfolio_view.php: -------------------------------------------------------------------------------- 1 |

Портфолио

2 |

3 | 4 | Все проекты в следующей таблице являются вымышленными, поэтому даже не пытайтесь перейти по приведенным ссылкам. 5 | 6 | '; 11 | } 12 | 13 | ?> 14 |
ГодПроектОписание
'.$row['Year'].''.$row['Site'].''.$row['Description'].'
15 |

16 | -------------------------------------------------------------------------------- /application/views/services_view.php: -------------------------------------------------------------------------------- 1 |

Услуги

2 |

3 | Компания ОЛОЛОША TEAM оказывает следующие услуги: 4 |

19 |

-------------------------------------------------------------------------------- /application/views/template_view.php: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | ОЛОЛОША TEAM 20 | 21 | 22 | 23 | 24 | 41 | 42 | 43 |
44 | 58 |
59 | 91 |
92 |
93 | 94 | 101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |

Наши контакты

109 |
    110 |
  • icq: 199199538
  • 111 |
  • skypeid: vitalyswipe
  • 112 |
  • email: vitalyswipe@gmail.com
  • 113 |
114 |
115 |
116 |

О Компании

117 |

118 | Вот дом. 119 | Который построил Джек. 120 | 121 | А это пшеница. 122 | Которая в тёмном чулане хранится 123 | В доме, 124 | Который построил Джек. 125 | 126 | А это весёлая птица-синица, 127 | Которая ловко ворует пшеницу, 128 | Которая в тёмном чулане хранится 129 | В доме, 130 | Который построил Джек. 131 | 132 | Вот кот, 133 | Который пугает и ловит синицу, 134 | Которая ловко ворует пшеницу, 135 | Которая в тёмном чулане хранится 136 | В доме, 137 | Который построил Джек. 138 |

139 |
140 |
141 |
142 |
143 | 146 | 147 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Design by Free CSS Templates 3 | http://www.freecsstemplates.org 4 | Released for free under a Creative Commons Attribution 3.0 License 5 | 6 | Name : Accumen 7 | Description: A two-column, fixed-width design with dark color scheme. 8 | Version : 1.0 9 | Released : 20120712 10 | */ 11 | 12 | * { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | a { 18 | text-decoration: underline; 19 | color: #353535; 20 | } 21 | 22 | a:hover { 23 | text-decoration: none; 24 | } 25 | 26 | body { 27 | line-height: 1.75em; 28 | background: #fff url('../images/00bd.png'); 29 | 30 | font-size: 11.5pt; 31 | color: #353535; 32 | } 33 | 34 | body,input { 35 | font-family: Kreon, serif; 36 | } 37 | 38 | br.clearfix { 39 | clear: both; 40 | } 41 | 42 | h1,h2,h3,h4 { 43 | text-transform: uppercase; 44 | font-weight: normal; 45 | font-family: "Open Sans", sans-serif; 46 | color: #353535; 47 | margin-bottom: 1em; 48 | } 49 | 50 | h1 { 51 | font-size: 1.75em; 52 | } 53 | 54 | h2 { 55 | font-size: 1.5em; 56 | } 57 | 58 | h3 { 59 | font-size: 1.25em; 60 | } 61 | 62 | h4 { 63 | font-size: 1em; 64 | } 65 | 66 | img.alignleft { 67 | float: left; 68 | margin: 5px 30px 20px 0; 69 | } 70 | 71 | img.aligntop { 72 | margin: 5px 0 20px 0; 73 | } 74 | 75 | p { 76 | margin-bottom: 1.5em; 77 | } 78 | 79 | ul { 80 | margin-bottom: 1.5em; 81 | } 82 | 83 | ul h4 { 84 | margin-bottom: 0.35em; 85 | } 86 | 87 | a { 88 | color: #353535; 89 | } 90 | 91 | #content { 92 | padding: 0 0 0 10px; 93 | width: 615px; 94 | margin: 0 0 0 285px; 95 | } 96 | 97 | #footer { 98 | padding: 20px 0 20px 0; 99 | text-align: center; 100 | text-shadow: 1px 1px 0px rgba(255,255,255,0.7); 101 | color: #353535; 102 | } 103 | 104 | #footer a { 105 | color: #353535; 106 | } 107 | 108 | #header { 109 | height: 130px; 110 | padding: 40px; 111 | position: relative; 112 | background: url(../images/cap.jpg) no-repeat 85px 30px; 113 | } 114 | 115 | #logo { 116 | position: absolute; 117 | top: 40px; 118 | left: 20px; 119 | height: 130px; 120 | line-height: 130px; 121 | 122 | } 123 | 124 | #logo a { 125 | color: #ffffff; 126 | text-shadow: black 0px 0px 7px; 127 | text-decoration: none; 128 | font-family: "Open Sans", sans-serif; 129 | font-size: 35px; 130 | } 131 | 132 | 133 | #logo img { 134 | margin-left: 70px; 135 | vertical-align: middle; 136 | } 137 | 138 | #menu { 139 | line-height: 57px; 140 | position: absolute; 141 | right: 40px; 142 | top: 76px; 143 | height: 57px; 144 | font-family: "Open Sans", sans-serif; 145 | } 146 | 147 | #menu a { 148 | text-transform: uppercase; 149 | text-decoration: underline; 150 | color: #1C1C1C; 151 | font-size: 1.2em; 152 | } 153 | 154 | #menu a:hover { 155 | text-decoration: none; 156 | } 157 | 158 | #menu ul { 159 | padding: 0 20px 0 20px; 160 | list-style: none; 161 | } 162 | 163 | #menu ul li { 164 | display: inline; 165 | padding: 10px 10px 10px 10px; 166 | margin: 0 8px 0 8px; 167 | } 168 | 169 | #page { 170 | margin: 0; 171 | position: relative; 172 | width: 900px; 173 | padding: 20px 40px 0 40px; 174 | } 175 | 176 | #page .section-list { 177 | padding-left: 0; 178 | list-style: none; 179 | } 180 | 181 | #page .section-list li { 182 | padding: 25px 0 25px 0; 183 | clear: both; 184 | } 185 | 186 | #sidebar ul { 187 | list-style: none; 188 | } 189 | 190 | #sidebar ul li { 191 | border-top: solid 1px #e3e3e3; 192 | padding: 10px 0 10px 0; 193 | } 194 | 195 | #sidebar ul li.first { 196 | padding-top: 0; 197 | border-top: 0; 198 | } 199 | 200 | #page-bottom { 201 | padding: 40px 40px 0 40px; 202 | color: #353535; 203 | background: #f3f3f3; 204 | position: relative; 205 | width: 898px; 206 | border-top: solid 1px #e3e3e3; 207 | box-shadow: inset 0px 0px 0px 1px #fff; 208 | text-shadow: 1px 1px 0px rgba(255,255,255,0.9); 209 | } 210 | 211 | #page-bottom a { 212 | color: #1B1A18; 213 | } 214 | 215 | #page-bottom h2, #page-bottom h3, #page-bottom h4 { 216 | color: #353535; 217 | } 218 | 219 | #page-bottom ul { 220 | list-style: none; 221 | } 222 | 223 | #page-bottom ul li { 224 | border-top: solid 1px #e3e3e3; 225 | padding: 10px 0 10px 0; 226 | } 227 | 228 | #page-bottom ul li.first { 229 | border-top: 0; 230 | padding-top: 0; 231 | } 232 | 233 | #page-bottom-content { 234 | width: 615px; 235 | margin: 0 0 0 290px; 236 | } 237 | 238 | #page-bottom-sidebar { 239 | float: left; 240 | width: 250px; 241 | } 242 | 243 | #sidebar { 244 | position: relative; 245 | left: -20px; 246 | top: -20px; 247 | float: left; 248 | width: 240px; 249 | background: #f3f3f3; 250 | padding: 20px; 251 | border: solid 1px #e3e3e3; 252 | margin: 0 10px 0 0; 253 | text-shadow: 1px 1px 0px rgba(255,255,255,1); 254 | box-shadow: inset 0px 0px 0px 1px #fff; 255 | } 256 | 257 | #wrapper { 258 | width: 978px; 259 | position: relative; 260 | background: #FFF; 261 | margin: 0 auto 0 auto; 262 | box-shadow: 10px 100px 150px 0px rgba(0,0,0,0.15); 263 | border: solid 1px #f3f3f3; 264 | border-top: 0; 265 | } 266 | 267 | .box img { 268 | margin: 0px 20px 0px 0px; 269 | border: solid 1px #e3e3e3; 270 | } 271 | 272 | .box p { 273 | margin-bottom: 0em; 274 | } 275 | 276 | .box ul{ 277 | margin-bottom: 0em; 278 | list-style: square inside; 279 | } 280 | 281 | .box table { 282 | border-collapse:collapse; 283 | } 284 | 285 | .box table, th, td { 286 | border: 1px solid #e3e3e3; 287 | padding: 5px; 288 | } 289 | -------------------------------------------------------------------------------- /images/00bd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitalyswipe/tinymvc/0b3b977daf18b91d917d437611db5d0c8fce57d6/images/00bd.png -------------------------------------------------------------------------------- /images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitalyswipe/tinymvc/0b3b977daf18b91d917d437611db5d0c8fce57d6/images/404.png -------------------------------------------------------------------------------- /images/cap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitalyswipe/tinymvc/0b3b977daf18b91d917d437611db5d0c8fce57d6/images/cap.jpg -------------------------------------------------------------------------------- /images/office-full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitalyswipe/tinymvc/0b3b977daf18b91d917d437611db5d0c8fce57d6/images/office-full.jpg -------------------------------------------------------------------------------- /images/office-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitalyswipe/tinymvc/0b3b977daf18b91d917d437611db5d0c8fce57d6/images/office-small.jpg -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 |