// connections
$connections = [
"mysql" => [
"driver" => "mysql",
"host" => "localhost",
"database" => $dbname,
"username" => $dbuser,
"password" => $dbpass,
"charset" => "utf8",
"collation" => "utf8_unicode_ci",
"prefix" => "",
],
];
$query = DB::table("developers")
->select("*")
->where("developers.age", ">", 30)
->whereNull("retired_at")
->orderBy("salary", "DESC")
->get();
$dsn = "{$dbtype}:dbname={$dbname};host={$host}";
$conn = new PDO($dsn, $dbuser, $dbpass);
//TODO: set charset, collation, table prefixes
$query = <<<QUERY
SELECT *
FROM developers
WHERE age > :age
AND WHERE retired_at IS NULL
ORDER BY salary DESC
QUERY;
$prep = $conn->prepare($query);
$prep->execute([":age" => 30]);
do_something_with_these_errors($prep->errorInfo());
Did anyone notice the SQL issue?
-- double quotes are MySQL specific
WHERE due_date < ":due_date"
errorInfo
every timeDB::table('developers')
// select these columns
->select('developers.first_name', 'projects.project_name')
// LEFT JOIN projects ON developers.id = projects.developer_id
->join('projects', 'developers.id', '=', 'projects.developer_id')
// add any number of conditions
->where('projects.due_date', '<', $due_date)
->whereNull('projects.completed_at')
// arbritrary clauses
->orderBy('projects.due_date', 'ASC')
// limit by X
->take($limit)
// perform the query
->get();
$query = <<<QUERY
SELECT first_name, project_name
FROM developers
INNER JOIN projects
ON developers.id = developer_id
WHERE due_date < ':due_date'
ORDER BY due_date ASC LIMIT :limit
QUERY;
$prep = $conn->prepare($query);
$prep->execute(
[":due_date" => $due_date, ":limit" => $limit]
);
do_something_with_these_errors($prep->errorInfo());
class User extends Eloquent
{
// don't need to add anything here
}
// grabbing all the users:
$all_users = User::all();
// grabbing the user with pk = 3
$user_no_3 = User::find(3);
// grabbing the first 10 teenage users
// organized by age (old -> young)
$teenagers = User::whereBetween("age", [13, 19])
->orderBy("age", "desc")
->take(10)
->get();
$user = new User();
$user->first_name = "Ciaran";
$user->last_name = "Downey";
$user->profession = "Developer";
try {
$primary_key = $user->save()
} catch (Exception $e) {
Log::error($e->getMessage());
throw $e;
}
class BaseRepo
{
private $conn;
public function __construct(PDO $conn) {
$this->conn = $conn;
}
// continued
public function fetchByQuery($query)
{
$stmt = $this->conn->prepare($query);
$stmt->execute();
return $stmt->fetch();
}
}
class UserRepo extends BaseRepo {
public function findByPk($key) {
$safe_key = intval($key);
return $this->fetchByQuery(
"SELECT * FROM users
WHERE id = {$safe_key}"
);
}
// continued
public function findAll()
{
return $this->fetchByQuery(
"SELECT * FROM users"
);
}
}
$repo = new UserRepo($db_from_somewhere);
// grab all the users
$all_users = $repo->findAll();
// grabbing the user with pk = 3
$user_no_3 = $repo->findByPk(3);
// grabbing the first 10 teenage users
// organized by age (old -> young)
$teenagers = $repo->fetchByQuery(
"SELECT * FROM users
WHERE age BETWEEN 13 AND 19
ORDER BY age DESC LIMIT 10"
);
$user = [
"first_name" => "Justin",
"last_name" => "Page",
"profession" => "Developer",
];
$query = <<<QUERY
INSERT INTO users
(first_name, last_name, profession)
VALUES
(':first', ':last', ':profession')
QUERY;
/*
now we just need to add:
- validation
- param binding
- nice error messages
- data transformation before / after save
*/
Use composer
{
"require": {
"laravel/framework": "~4.2.3"
},
"autoload": {
"psr-4": {"MyNamespace\\": "src/"}
},
"config": { "preferred-install": "dist" },
"minimum-stability": "stable"
}
<?php // entry file
require __DIR__ . "/vendor/autoload.php";
// as long as the autoload file has been included
// before, we don't have to specify anything else
$cat = new MyNamespace\Model\Cat();
// loaded only when used = lazy loading
$hat = new MyNamespace\Fashion\Hat();
$cat->moveInside($hat);
// ever seen something like this?
require "../inc/libs/_globals/bootstrap_NEW.php";
// or this?
foreach ($required_files as $file) {
require $file;
}
// or this?
foreach (glob("../lib/*.php") as $file) {
require $file;
}
Route::get('/blog', function() {
// do blog stuff
});
// or
Route::get("/blog", "BlogController@showBlog");
// different request methods
Route::get("/blog/post/{id}", function ($id) {
// show that post
});
Route::delete("/blog/post/{id}", function ($id) {
// delete that post
});
+-- about
| +-- index.php
+-- blog
+-- index.php
+-- post
+-- index.php
switch (strtoupper($_SERVER["REQUEST_METHOD")) {
"GET":
// show the post
break;
"DELETE":
// do delete stuff
break;
default:
// show a 404
}
CSRF is an attack which forces an end user to execute unwanted actions on a web application in which he/she is currently authenticated
<input type="hidden"
name="_token"
value="<?php echo csrf_token(); ?>">
Route::post('register', [
// filters to apply
'before' => 'csrf',
function() {
return 'You gave a valid CSRF token!';
}
]);
// Or
Route::post('register',
["before" => "csrf", "Controller@register"]
);
// generates the csrf token
function csrf_token()
{
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
// lolwat
$_SESSION["token"] =
md5(uniqid(mt_rand(), true)));
return $_SESSION["token"];
}
<input type="hidden"
name="_token"
value="<?php echo csrf_token(); ?>">
function check_token()
{
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
// works with both POST and GET
$req = count($_POST) ? $_POST : $_GET;
return isset($req["_token"])
&& $req["_token"] !== $_SESSION["token"];
}
// now just call that in every file you need it
if (check_token()) {
show_error_page_or_whatever_we_do();
}
// except...
// remember this line?
$_SESSION["token"] =
md5(uniqid(mt_rand(), true)));
// are you sure that's secure?
// Here's what Laravel does
$random = 'openssl_random_pseudo_bytes';
if (function_exists($random)) {
$bytes = $random($length * 2);
$base64 = base64_encode($bytes);
return substr(
str_replace(['/', '+', '='], '', $base64),
0,
$length
);
}
// or if we don't have openssl_random_pseudo_bytes
} else {
$pool = '0123456789'
. 'abcdefghipqrstuvwxyz'
. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(
str_shuffle(str_repeat($pool, 5)),
0,
$length
);
}
How many of you have written functions like this?
Who can tell us what these parameters mean?
Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can't break.
// Carbon comes included with Laravel
use Carbon\Carbon;
Carbon::now()->diffForHumans(
Carbon::parse("July 4th, 2014")
);
// => "1 week before"
// other cool things
Carbon::yesterday();
Carbon::now()->isWeekend();
Carbon::now()->subWeekdays(5);
die("nope.gif");
<html lang='en'><!-- app/views/master.php -->
<head>
<title><?= Section::yield("title") ?></title>
</head>
<body>
<div class="container">
<?= Section::yield("content") ?>
</div>
<?php if ($projects): ?>
<?= Section::yield("js") ?>
<?php endif; ?>
</body>
</html>
<?php include __DIR__ . "/../master.php";
// include the master template
// start a new section called "content"
Section::startSection("content"); ?>
Nothing magic here! Everything gets rendered in
the master template above. You get access to all
the things you'd normally have access to. See?
The year is: <?= date("Y") ?>
<?php // and just stop the section when you're done
Section::stopSection("content");
<html lang='en'><!-- app/views/master.php -->
<head>
<title><?= $title ?></title>
</head>
<body>
<div class="container">
<?= $content ?>
</div>
<?php if ($projects): ?>
<?= $js ?>
<?php endif; ?>
</body>
</html>
<?php /* start output buffering */ ob_start(); ?>
Nothing magic here! Everything gets rendered in
the master template above. You get access to all
the things you'd normally have access to. See?
The year is: <?= date("Y") ?>
<?php
$content = ob_get_clean();
$title = "My Cool Title";
$projects = [];
include __DIR__ . "/../master.php";
ob_start()
at the top of each section
<head>
tag is
really hard./artisan migrate:install
./artisan migrate:make create_projects_table
app/database/migrations
2014_06_23_073419_create_developers_table.php
public function up()
{
// create a "projects" table and apply these features
Schema::create('projects', function(Blueprint $table) {
// primary key named 'id'
$table->increments('id');
// a unique string named 'project_name'
$table->string('project_name')->unique();
$table->string('developer_id');
$table->dateTime('due_date');
$table->dateTime('completed_at')->nullable();
// "created_at" and "updated_at" columns
$table->timestamps();
});
}
public function down()
{
// drop the table
Schema::drop('projects');
}
./artisan migrate:migrate
./artisan migrate:reset
due_date
column to a projects table:
./artisan migrate:make \
`# the name of the migration` \
add_due_dates_to_projects_table \
`# the table to generate a migration for` \
--table=projects
Route::resource('developers', 'DevelopersController');
Verb | Path | Action | Route Name |
---|---|---|---|
GET | /resource | index | resource.index |
GET | /resource/create | create | resource.create |
POST | /resource | store | resource.store |
GET | /resource/{resource} | show | resource.show |
GET | /resource/{resource}/edit | edit | resource.edit |
PUT/PATCH | /resource/{resource} | update | resource.update |
DELETE | /resource/{resource} | destroy | resource.destroy |
class DevelopersController
{
// show an index page for this resource
public function index();
// show a create form for this resource
public function create();
// store a resource
public function store();
// show the requested resource
public function show($id);
// show an edit form for the requested resource
public function edit($id);
// update the requested resource
public function update($id);
// delete the requested resource
public function destroy($id);
}
public function index()
{
$devs = Developer::all();
return View::make('developers.index', ["devs" => $devs]);
}
public function index()
{
$devs = Developer::all();
return View::make('developers.index', ["devs" => $devs]);
}
public function store()
{
$dev = new Developer;
$dev->first_name = Input::only("first_name");
$dev->last_name = Input::only("last_name");
$dev->save();
return Redirect::route('developers.index');
}
public function store()
{
Developer::create(Input::all());
return redirect::route('developers.index');
}
Guard against mass assignment vulnerability with a whitelist
class Developer extends Eloquent
{
protected $fillable = ['first_name', 'last_name'];
}
public function show($id)
{
// shows a 404 if it fails
$dev = Developer::findOrFail($id);
return View::make('developers.show', ['dev' => $dev]);
}
public function edit($id)
{
// shows a 404 if it fails
$dev = Developer::findOrFail($id);
return View::make('developers.edit', ["dev" => $dev]);
}
<?= Form::model($dev,
["method" => "PATCH", "route" => "developers.update", $dev->id]
) ?>
<div>
<?= Form::label('first_name', 'First Name:') ?>
<?= Form::text('first_name') ?>
</div>
<div>
<?= Form::label('last_name', 'Last Name:') ?>
<?= Form::text('last_name') ?>
</div>
<div><?= Form::submit('Update Developer') ?></div>
<?= Form::close() ?>
public function update($id)
{
$dev = Developer::findOrFail($id);
$dev->fill(Input::all());
$dev->save();
return Redirect::route('developers.show', ['id' => $id]);
}
public function destroy($id)
{
// grab the model first
Developer::findOrFail($id)->delete();
// OR
$affectedRows = Developer::destroy($id);
return Redirect::route('developers.index');
}
// Redirect to a route
return Redirect::to('developer/login');
// Redirect to a named route
return Redirect::to('login');
// Redirect to a Controller action
return Redirect::action('DevelopersController@index');
class DeveloperTableSeeder extends Seeder
{
public function run()
{
Developer::truncate();
Developer::create([
'email' => 'ciaran@ciarandowney.com',
'username' => 'ciarand',
'password' => Hash::make('1234'),
]);
}
}
Route::get('login', 'AuthController@login');
Route::post('login', 'AuthController@processLogin');
Route::get('logout', 'AuthController@logout');
// we don't want just anyone to access this
Route::get('projects', function() {
return 'List of Super Secret Projects';
// so we run the auth filter before showing it to anyone
})->before('auth');
// the auth filter
Route::filter('auth', function() {
if (Auth::guest()) {
// sneaky sneaky
return Redirect::guest('login');
}
});
public function login()
{
if (Auth::check()) {
// if they're already logged in they shouldn't be here
return Redirect::to('/projects');
}
return View::make('developers.login');
}
public function processLogin()
{
if (Auth::attempt(Input::only('email', 'password'))) {
return Redirect::to('/projects')
->with('message', 'Successfully logged in');
}
return Redirect::back()
->with('message', 'Invalid credentials')
->withInput();
}
public function logout()
{
Auth::logout();
return Redirect::home()->with('message', 'Logged out');
}
Dependency Injection" is a 25-dollar term for a 5-cent concept. [...] Dependency injection means giving an object its instance variables. [...].
class DevelopersController extends BaseController
{
public function index()
{
$devs = Developer::all();
return View::make('developers.index', ["devs" => $devs]);
}
}
interface DeveloperRepositoryInterface
{
public function all();
}
class DBDeveloperRepository implements DeveloperRepositoryInterface
{
protected function all()
{
return Developer::all();
}
}
class DevelopersController extends BaseController
{
protected $repo;
public function __construct(DeveloperRepositoryInterface $repo)
{
$this->repo = $repo;
}
public function index()
{
$devs = $this->repo->all();
return View::make('developers.index', ["devs" => $devs]);
}
}
App::bind( // bind this interface
'DeveloperRepositoryInterface',
'DBDeveloperRepository' // to this implementation
);
// Sometimes you may wish to resolve only one instance of a given
// class throughout your entire application.
App::singleton( // bind this interface
'DeveloperRepositoryInterface',
'DBDeveloperRepository' // to the same object
);
// Or you may wish to pass through an already existing instance
App::singleton('DeveloperRepositoryInterface', $repo);
/