[Laravel] laravel filesystem 使用 public 的 storage

在 config/filesystems.php 檔案中,將:

‘default’ => ‘local’,
改成
‘default’ => ‘public‘,

代表將會把上傳的圖片,存到 storage/app/public 資料夾中。

然後要再執行以下指令來建立symbolic link:

# cd ~/path/project_folder
# ln -s ../storage/app/public ./public/storage

就可以在網頁中使用以下語法,來取得路徑:

<img src=”{{ asset(‘storage/file.jpg’) }}”>

[Laravel] ClearCompiledCommand.php

在執行 $ composer update 時,若遇到以下錯誤訊息:

[RuntimeException]                                                           
  Error Output: PHP Fatal error:  Call to undefined method Illuminate\Foundation\Application::getCachedCompilePath() in /.../path/.../vendor/laravel/framework/src/Illuminate/Foundation/Console/ClearCompiledCommand.php on line 28

vendor/compiled.php 檔案移除,再重新執行 $ composer update 即可。

[Laravel] use php artisan generate

參考:Laravel-4-Generators

// 建立tags資料表,欄位有name,型別用string代表VARCHAR
$ php artisan generate:migration create_tags_table --fields="name:string"

// 將資料表s_deposit更換成另一個名稱。使用php artisan migrate:make單純建立一個migrate檔
$ php artisan migrate:make rename_s_deposit_table

// 建立lessons與tags的多對多的關聯資料表
$ php artisan generate:pivot lessons tags

// 執行尚未執行的migrate檔
$ php artisan migrate

// 替tags資料表建立seed檔:準備建立假資料
$ php artisan generate:seed tags

// 重新執行所有的migrate檔及seed檔
$ php artisan migrate:refresh --seed

// 建立產生資料表的migrate檔
$ php artisan generate:migration create_app_version_table

// 建立controller檔
$ php artisan controller:make TagsController

// 建立一個將deposit欄位新增至users資料表的migration檔
$ php artisan generate:migration add_deposit_to_users_table

[Laravel] Basic Authentication

1. update app/controllers/LessonsController.php

<?php
use Acme\Transformers\LessonTransformer;

class LessonsController extends ApiController {
    
    /**
     * @var My\Transformers\LessonTransformer
     */
    protected $lessonTransformer;
    
    function __construct(LessonTransformer $lessonTransformer)
    {
        $this->lessonTransformer = $lessonTransformer;
        
        $this->beforeFilter('auth.basic', ['on' => 'post']);
        // or use this for multi http verb to auth.basic
        // $this->beforeFilter('auth.basic', array('on' => array('post','get')) );
    }
    
    
  /**
   * Display a listing of the resource.
   *
   * @return Response
   */
  public function index()
  {
    // 1. All is bad
    // 2. No way to attach meta data
    // 3. Linking db structure to the API output
    // 4. No way to signal headers/response codes
    
    //return Lesson::all();
  
    $lessons = Lesson::all();
    //return Response::json([
      //'data' => $lessons->toArray()
    //], 200);
    //return Response::json([
      //'data' => $this->transformCollection($lessons)
    //], 200);
    //return Response::json([
      //'data' => $this->lessonTransformer->transformCollection($lessons->all())
    //], 200);
    return $this->respond([
      'data' => $this->lessonTransformer->transformCollection($lessons->all())
    ]);
  }


  /**
   * Show the form for creating a new resource.
   *
   * @return Response
   */
  public function create()
  {
    //
  }


  /**
   * Store a newly created resource in storage.
   *
   * @return Response
   */
  public function store()
  {
    if( ! Input::get('title') or ! Input::get('body') ){
        return $this->setStatusCode(422)
                    ->respondWithError('Parameters failed validation for a lesson.');
    }
    
    Lesson::create(Input::all());
    
    return $this->respondCreated('Lesson successfully created.');
  }


  /**
   * Display the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function show($id)
  {
    $lesson = Lesson::find($id);
    if( ! $lesson)
    {
        return $this->respondNotFound('Lesson does not exist.');
        
      //return Response::json([
        //'error' => [
          //'message' => 'Lesson does not exist'
        //]
      //], 404);
    }
    
    return $this->respond([
        'data' => $this->lessonTransformer->transform($lesson)
        //'data' => $this->transform($lesson->toArray())
    ]);
  }


  /**
   * Show the form for editing the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function edit($id)
  {
    //
  }


  /**
   * Update the specified resource in storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function update($id)
  {
    //
  }


  /**
   * Remove the specified resource from storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function destroy($id)
  {
    //
  }


}

2. update app/controllers/ApiController.php

<?php

use Illuminate\Http\Response as IlluminateResponse;

class ApiController extends BaseController{
    
    /**
     * getStatusCode function.
     * 
     * @access public
     * @return void
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }
    
    /**
     * setStatusCode function.
     * 
     * @access public
     * @param mixed $statusCode
     * @return void
     */
    public function setStatusCode($statusCode)
    {
        $this->statusCode = $statusCode;
        
        return $this;
    }
    
    /**
     * respondNotFound function.
     * 
     * @access public
     * @param string $message (default: 'Not Found!')
     * @return void
     */
    public function respondNotFound($message = 'Not Found!')
    {
        return $this->setStatusCode(IlluminateResponse::HTTP_NOT_FOUND)->respondWithError($message);
    }
    
    /**
     * respondInternalError function.
     * 
     * @access public
     * @param string $message (default: 'Internal Error!')
     * @return void
     */
    public function respondInternalError($message = 'Internal Error!')
    {
        return $this->setStatusCode(IlluminateResponse::HTTP_INTERNAL_SERVER_ERROR)->respondWithError($message);
    }
    
    /**
     * respond function.
     * 
     * @access public
     * @param mixed $data
     * @param mixed $headers (default: [])
     * @return void
     */
    public function respond($data, $headers = [])
    {
        return Response::json($data, $this->getStatusCode(), $headers);
    }
    
    public function respondWithError($message)
    {
        return $this->respond([
            'error' => [
                'message' => $message,
                'status_code' => $this->getStatusCode()
            ]
        ]);
    }
    
    /**
     * respondCreated function.
     * 
     * @access public
     * @param mixed $message
     * @return void
     */
    public function respondCreated($message)
    {
        return $this->setStatusCode(IlluminateResponse::HTTP_CREATED)->respond([
            'message' =>  $message
        ]);
    }
    
}

[Laravel] Refactoring

1. add app/controllers/ApiController.php

<?php
class ApiController extends BaseController{
    protected $statusCode = 200;
    
    /**
     * getStatusCode function.
     * 
     * @access public
     * @return void
     */
    public function getStatusCode()
    {
        return $this--->statusCode;
    }
    
    /**
     * setStatusCode function.
     * 
     * @access public
     * @param mixed $statusCode
     * @return void
     */
    public function setStatusCode($statusCode)
    {
        $this->statusCode = $statusCode;
        
        return $this;
    }
    
    /**
     * respondNotFound function.
     * 
     * @access public
     * @param string $message (default: 'Not Found!')
     * @return void
     */
    public function respondNotFound($message = 'Not Found!')
    {
        return $this->setStatusCode(404)->respondWithError($message);
    }
    
    /**
     * respondInternalError function.
     * 
     * @access public
     * @param string $message (default: 'Internal Error!')
     * @return void
     */
    public function respondInternalError($message = 'Internal Error!')
    {
        return $this->setStatusCode(500)->respondWithError($message);
    }
    
    /**
     * respond function.
     * 
     * @access public
     * @param mixed $data
     * @param mixed $headers (default: [])
     * @return void
     */
    public function respond($data, $headers = [])
    {
        return Response::json($data, $this->getStatusCode(), $headers);
    }
    
    public function respondWithError($message)
    {
        return $this->respond([
            'error' => [
                'message' => $message,
                'status_code' => $this->getStatusCode()
            ]
        ]);
    }
}

2. update app/controllers/LessonsController.php

<?php
use Acme\Transformers\LessonTransformer;

class LessonsController extends ApiController {
    
    /**
     * @var My\Transformers\LessonTransformer
     */
    protected $lessonTransformer;
    
    function __construct(LessonTransformer $lessonTransformer)
    {
        $this->lessonTransformer = $lessonTransformer;
    }
    
    
  /**
   * Display a listing of the resource.
   *
   * @return Response
   */
  public function index()
  {
    // 1. All is bad
    // 2. No way to attach meta data
    // 3. Linking db structure to the API output
    // 4. No way to signal headers/response codes
    
    //return Lesson::all();
  
    $lessons = Lesson::all();
    //return Response::json([
      //'data' => $lessons->toArray()
    //], 200);
    //return Response::json([
      //'data' => $this->transformCollection($lessons)
    //], 200);
    //return Response::json([
      //'data' => $this->lessonTransformer->transformCollection($lessons->all())
    //], 200);
    return $this->respond([
      'data' => $this->lessonTransformer->transformCollection($lessons->all())
    ]);
  }


  /**
   * Show the form for creating a new resource.
   *
   * @return Response
   */
  public function create()
  {
    //
  }


  /**
   * Store a newly created resource in storage.
   *
   * @return Response
   */
  public function store()
  {
    //
  }


  /**
   * Display the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function show($id)
  {
    $lesson = Lesson::find($id);
    if( ! $lesson)
    {
        return $this->respondNotFound('Lesson does not exist.');
        
      //return Response::json([
        //'error' => [
          //'message' => 'Lesson does not exist'
        //]
      //], 404);
    }
    
    return $this->respond([
        'data' => $this->lessonTransformer->transform($lesson)
        //'data' => $this->transform($lesson->toArray())
    ]);
  }


  /**
   * Show the form for editing the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function edit($id)
  {
    //
  }


  /**
   * Update the specified resource in storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function update($id)
  {
    //
  }


  /**
   * Remove the specified resource from storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function destroy($id)
  {
    //
  }


}

[Laravel] Extraction

1. ex: make a file app/Acme/Transformers/Transformer.php

<?php namespace Acme\Transformers;
    
abstract class Transformer{
    public function transformCollection(array $items)
    {
        return array_map([$this, 'transform'], $items);
    }
    
    public abstract function transform($item);
}

2. ex: make a file app/Acme/Transformers/LessonTransformer.php

 $lesson['title'],
            'body' => $lesson['body'],
            'active' => (boolean) $lesson['some_bool']
        ];
    }
}

3. update composer.json: add app/Acme to classmap

"autoload": {
		"classmap": [
			"app/commands",
			"app/controllers",
			"app/models",
			"app/database/migrations",
			"app/database/seeds",
			"app/tests/TestCase.php",
			
			"app/Acme"
		]
	},

4. update app/controllers/LessonsController.php

<?php
use Acme\Transformers\LessonTransformer;

class LessonsController extends \BaseController {
    
    /**
     * @var My\Transformers\LessonTransformer
     */
    protected $lessonTransformer;
    
    function __construct(LessonTransformer $lessonTransformer)
    {
        $this->lessonTransformer = $lessonTransformer;
    }

public function index()
  {
    // 1. All is bad
    // 2. No way to attach meta data
    // 3. Linking db structure to the API output
    // 4. No way to signal headers/response codes
    
    //return Lesson::all();
  
    $lessons = Lesson::all();
    //return Response::json([
      //'data' => $lessons->toArray()
    //], 200);
    //return Response::json([
      //'data' => $this->transformCollection($lessons)
    //], 200);
    return Response::json([
      'data' => $this->lessonTransformer->transformCollection($lessons->all())
    ], 200);
  }

public function show($id)
  {
    $lesson = Lesson::find($id);
    if( ! $lesson)
    {
      return Response::json([
        'error' => [
          'message' => 'Lesson does not exist'
        ]
      ], 404);
    }
    
    return Response::json([
        'data' => $this->lessonTransformer->transform($lesson)
        //'data' => $this->transform($lesson->toArray())
    ], 200);
  }

}

[Laravel] Transformations

1. update app/controllers/LessonsController.php

public function index()
{
  // 1. All is bad
  // 2. No way to attach meta data
  // 3. Linking db structure to the API output
  // 4. No way to signal headers/response codes
    
  //return Lesson::all();
  
  $lessons = Lesson::all();
  //return Response::json([
    //'data' => $lessons->toArray()
  //], 200);
  return Response::json([
    'data' => $this->transformCollection($lessons);
  ], 200);
}

public function show($id)
{
  $lesson = Lesson::find($id);
  if( ! $lesson)
  {
    return Response::json([
      'error' => [
        'message' => 'Lesson does not exist'
      ]
    ], 404);
  }
    
  return Response::json([
    'data' => $this->transform($lesson->toArray())
  ], 200);
}


public function transformCollection($lessons)
{
  return array_map([$this, 'transform'], $lessons->toArray());
}
  
public function transform($lesson)
{
    return [
        'title' => $lesson['title'],
        'body' => $lesson['body'],
        'active' => (boolean) $lesson['some_bool']
    ];
}

[Laravel] Responses and Codes

1. update app/controllers/LessonsController.php
check http status code

public function index()
{
  // 1. All is bad
  // 2. No way to attach meta data
  // 3. Linking db structure to the API output
  // 4. No way to signal headers/response codes
    
  //return Lesson::all();
  
  $lessons = Lesson::all();
  return Response::json([
    'data' => $lessons->toArray()
  ], 200);  //200 also can use 404(not found)
}

2. use curl to test

$ curl -i http://localhost:8000/api/v1/lessons
// or you can use "python -mjson.tool" to show more human readable
$ curl http://localhost:8000/api/v1/lessons | python -mjson.tool

Screen Shot 2014-05-17 at 8.31.50 AM

3. update show function to test specific id
try: http://localhost:8000/api/v1/lessons/5 or try: http://localhost:8000/api/v1/lessons/50

public function show($id)
{
  $lesson = Lesson::find($id);
  if( ! $lesson)
  {
    return Response::json([
      'error' => [
        'message' => 'Lesson does not exist'
      ]
    ], 404);
  }
    
  return Response::json([
    'data' => $lesson->toArray()
  ], 200);
}

[Laravel] queries to JSON

1. update app/routes.php

Route::group(['prefix' => 'api/v1'], function()
{
  Route::resource("lessons", "LessonsController");
});

2. create LessonsController

$ php artisan controller:make LessonsController

3. check: php artisan routes

$ php artisan routes

Screen Shot 2014-05-16 at 12.20.58 AM

4. update app/controllers/LessonsController.php

public function index()
{
  // 1. All is bad
  // 2. No way to attach meta data
  // 3. Linking db structure to the API output
  // 4. No way to signal headers/response codes
  return Lesson::all();
}

5. browse http://localhost:8000/api/v1/lessons: get json data.(this is bad practice only for test)

Screen Shot 2014-05-16 at 12.28.54 AM

6. install chrome extension: postman

Screen Shot 2014-05-16 at 12.35.43 AM

7. hide some data if you do not show it: use $hidden

<?php
class Lesson extends \Eloquent{
  protected $fillable = ['title', 'body'];
  
  protected $hidden = ['title'];
}

[Laravel] create dummy data

1、Suggested that install Generators first.

2、create a table called lessons

$ php artisan generate:migration create_lessons_table --fields="title:string, body:text"
$ php artisan migrate

3、create a seed table file “LessonsTableSeeder.php” in app/database/seeds directory, the code:

<?php

use Faker\Factory as Faker;

class LessonsTableSeeder extends Seeder {

  
  public function run()
  {
    $faker = Faker::create();
    foreach( range(1, 10) as $index )
    {
      Lesson::create([
        
      ]);
    }
  }

}

4、search which library can create dummy data and install it:

$ composer search faker
fzaninotto/faker Faker is a PHP library that generates fake data for you.

$ composer require fzaninotto/faker --dev
Please provide a version constraint for the fzaninotto/faker requirement: 1.3
// then will update composer.json to
"require-dev": {
  "fzaninotto/faker": "1.3"
}

5、update app/database/seeds/LessonsTableSeeder.php

public function run()
{
  $faker = Faker::create();
  foreach( range(1, 30) as $index )
  {
    Lesson::create([
      'title' => $faker->sentence(5),
      'body' => $faker->paragraph(4)
    ]);
  }
}

6、update app/database/seeds/DatabaseSeeder.php

public function run()
{
  Lesson::truncate();

  Eloquent::unguard();

  // $this->call('UserTableSeeder');
  $this->call('LessonsTableSeeder');
}

7、create new file app/models/Lesson.php and update:

<?php
class Lesson extends \Eloquent{
  protected $fillable = ['title', 'body'];
}

7、execute command to generate dummy data

$ php artisan db:seed
Screen Shot 2014-05-13 at 9.48.31 PM