view.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. <?php namespace Laravel;
  2. use Closure;
  3. use Laravel\Validation\Messages;
  4. class View {
  5. /**
  6. * The name of the view.
  7. *
  8. * @var string
  9. */
  10. public $view;
  11. /**
  12. * The view data.
  13. *
  14. * @var array
  15. */
  16. public $data;
  17. /**
  18. * The path to the view on disk.
  19. *
  20. * @var string
  21. */
  22. protected $path;
  23. /**
  24. * All of the view composers for the application.
  25. *
  26. * @var array
  27. */
  28. protected static $composers;
  29. /**
  30. * Create a new view instance.
  31. *
  32. * @param string $view
  33. * @param array $data
  34. * @return void
  35. */
  36. public function __construct($view, $data = array())
  37. {
  38. $this->view = $view;
  39. $this->data = $data;
  40. $this->path = $this->path($view);
  41. // If a session driver has been specified, we will bind an instance of
  42. // the validation error message container to every view. If an errors
  43. // instance exists in the session, we will use that instance.
  44. //
  45. // This makes the implementation of the Post/Redirect/Get pattern very
  46. // convenient since each view can assume it has a message container.
  47. if (Config::$items['session']['driver'] !== '' and IoC::core('session')->started())
  48. {
  49. $this->data['errors'] = IoC::core('session')->get('errors', function()
  50. {
  51. return new Messages;
  52. });
  53. }
  54. }
  55. /**
  56. * Get the path to a given view on disk.
  57. *
  58. * @param string $view
  59. * @return string
  60. */
  61. protected function path($view)
  62. {
  63. $view = str_replace('.', '/', $view);
  64. // Application views take priority over system views, so we will return the
  65. // application version of the view if both exists. Also, we need to check
  66. // for Blade views using the Blade extension. The Blade extension gives
  67. // an easy method of determining if the view should be compiled using
  68. // the Blade compiler or if the view is plain PHP.
  69. foreach (array(EXT, BLADE_EXT) as $extension)
  70. {
  71. if (file_exists($path = VIEW_PATH.$view.$extension))
  72. {
  73. return $path;
  74. }
  75. elseif (file_exists($path = SYS_VIEW_PATH.$view.$extension))
  76. {
  77. return $path;
  78. }
  79. }
  80. throw new \Exception("View [$view] does not exist.");
  81. }
  82. /**
  83. * Create a new view instance.
  84. *
  85. * The name of the view given to this method should correspond to a view
  86. * within your application views directory. Dots or slashes may used to
  87. * reference views within sub-directories.
  88. *
  89. * <code>
  90. * // Create a new view instance
  91. * $view = View::make('home.index');
  92. *
  93. * // Create a new view instance with bound data
  94. * $view = View::make('home.index', array('name' => 'Taylor'));
  95. * </code>
  96. *
  97. * @param string $view
  98. * @param array $data
  99. * @return View
  100. */
  101. public static function make($view, $data = array())
  102. {
  103. return new static($view, $data);
  104. }
  105. /**
  106. * Create a new view instance from a view name.
  107. *
  108. * View names are defined in the application composers file.
  109. *
  110. * <code>
  111. * // Create an instance of the "layout" named view
  112. * $view = View::of('layout');
  113. *
  114. * // Create an instance of the "layout" view with bound data
  115. * $view = View::of('layout', array('name' => 'Taylor'));
  116. * </code>
  117. *
  118. * @param string $name
  119. * @param array $data
  120. * @return View
  121. */
  122. public static function of($name, $data = array())
  123. {
  124. if ( ! is_null($view = static::name($name)))
  125. {
  126. return static::make($view, $data);
  127. }
  128. throw new \Exception("Named view [$name] is not defined.");
  129. }
  130. /**
  131. * Find the key for a view by name.
  132. *
  133. * The view "key" is the string that should be passed into the "make" method and
  134. * should correspond with the location of the view within the application views
  135. * directory, such as "home.index" or "home/index".
  136. *
  137. * @param string $name
  138. * @return string
  139. */
  140. protected static function name($name)
  141. {
  142. static::composers();
  143. foreach (static::$composers as $key => $value)
  144. {
  145. if ($name === $value or $name === Arr::get((array) $value, 'name'))
  146. {
  147. return $key;
  148. }
  149. }
  150. }
  151. /**
  152. * Call the composer for the view instance.
  153. *
  154. * @param View $view
  155. * @return void
  156. */
  157. protected static function compose(View $view)
  158. {
  159. static::composers();
  160. if (isset(static::$composers[$view->view]))
  161. {
  162. foreach ((array) static::$composers[$view->view] as $key => $value)
  163. {
  164. if ($value instanceof Closure) return call_user_func($value, $view);
  165. }
  166. }
  167. }
  168. /**
  169. * Load the view composers for the application.
  170. *
  171. * For better testing flexiblity, we load the composers from the IoC container.
  172. *
  173. * @return void
  174. */
  175. protected static function composers()
  176. {
  177. static::$composers = require APP_PATH.'composers'.EXT;
  178. }
  179. /**
  180. * Get the evaluated string content of the view.
  181. *
  182. * @return string
  183. */
  184. public function render()
  185. {
  186. static::compose($this);
  187. // All nested views and responses are evaluated before the main view.
  188. // This allows the assets used by the nested views to be added to the
  189. // asset container before the main view is evaluated and dumps the
  190. // links to the assets.
  191. foreach ($this->data as &$data)
  192. {
  193. if ($data instanceof View or $data instanceof Response)
  194. {
  195. $data = $data->render();
  196. }
  197. }
  198. ob_start() and extract($this->data, EXTR_SKIP);
  199. // If the view is Bladed, we need to check the view for modifications
  200. // and get the path to the compiled view file. Otherwise, we'll just
  201. // use the regular path to the view.
  202. $view = (strpos($this->path, BLADE_EXT) !== false) ? $this->compile() : $this->path;
  203. try { include $view; } catch (\Exception $e) { ob_get_clean(); throw $e; }
  204. return ob_get_clean();
  205. }
  206. /**
  207. * Compile the Bladed view and return the path to the compiled view.
  208. *
  209. * @return string
  210. */
  211. protected function compile()
  212. {
  213. // For simplicity, compiled views are stored in a single directory by
  214. // the MD5 hash of their name. This allows us to avoid recreating the
  215. // entire view directory structure within the compiled views directory.
  216. $compiled = STORAGE_PATH.'views/'.md5($this->view);
  217. // The view will only be re-compiled if the view has been modified
  218. // since the last compiled version of the view was created or no
  219. // compiled view exists. Otherwise, the path will be returned
  220. // without re-compiling.
  221. if ( ! file_exists($compiled) or (filemtime($this->path) > filemtime($compiled)))
  222. {
  223. file_put_contents($compiled, Blade::compile($this->path));
  224. }
  225. return $compiled;
  226. }
  227. /**
  228. * Add a view instance to the view data.
  229. *
  230. * <code>
  231. * // Add a view instance to a view's data
  232. * $view = View::make('foo')->nest('footer', 'partials.footer');
  233. *
  234. * // Equivalent functionality using the "with" method
  235. * $view = View::make('foo')->with('footer', View::make('partials.footer'));
  236. *
  237. * // Bind a view instance with data
  238. * $view = View::make('foo')->nest('footer', 'partials.footer', array('name' => 'Taylor'));
  239. * </code>
  240. *
  241. * @param string $key
  242. * @param string $view
  243. * @param array $data
  244. * @return View
  245. */
  246. public function nest($key, $view, $data = array())
  247. {
  248. return $this->with($key, static::make($view, $data));
  249. }
  250. /**
  251. * Add a key / value pair to the view data.
  252. *
  253. * Bound data will be available to the view as variables.
  254. *
  255. * @param string $key
  256. * @param mixed $value
  257. * @return View
  258. */
  259. public function with($key, $value)
  260. {
  261. $this->data[$key] = $value;
  262. return $this;
  263. }
  264. /**
  265. * Magic Method for getting items from the view data.
  266. */
  267. public function __get($key)
  268. {
  269. return $this->data[$key];
  270. }
  271. /**
  272. * Magic Method for setting items in the view data.
  273. */
  274. public function __set($key, $value)
  275. {
  276. $this->with($key, $value);
  277. }
  278. /**
  279. * Magic Method for determining if an item is in the view data.
  280. */
  281. public function __isset($key)
  282. {
  283. return array_key_exists($key, $this->data);
  284. }
  285. /**
  286. * Magic Method for removing an item from the view data.
  287. */
  288. public function __unset($key)
  289. {
  290. unset($this->data[$key]);
  291. }
  292. /**
  293. * Magic Method for handling the dynamic creation of named views.
  294. *
  295. * <code>
  296. * // Create an instance of the "layout" named view
  297. * $view = View::of_layout();
  298. *
  299. * // Create an instance of a named view with data
  300. * $view = View::of_layout(array('name' => 'Taylor'));
  301. * </code>
  302. */
  303. public static function __callStatic($method, $parameters)
  304. {
  305. if (strpos($method, 'of_') === 0)
  306. {
  307. return static::of(substr($method, 3), Arr::get($parameters, 0, array()));
  308. }
  309. }
  310. }