Laravel and angular cheatsheet
Mount linux file system under Macos:
sudo fuse-ext2 /dev/disk3s1 /mnt/disk2s1 -o force
Laravel frequently used references:
https://phpdocker.io/generator
https://laravel.com/docs/5.8/queries
https://laravel.com/docs/5.8/eloquent
https://blog.csdn.net/chenrui310/article/details/80664418 ORM methods
https://laravel.com/docs/master/helpers
https://github.com/Metabuyer/laravel-permission-mongodb
https://github.com/designmynight/laravel-mongodb-passport
https://github.com/jenssegers/laravel-mongodb
http://andersonandra.de/l5-repository
https://laravel.com/docs/5.8/passport
Illuminate\Support\Str : https://learnku.com/articles/16807
contracts vs facades list:
https://learnku.com/docs/laravel/5.5/contracts/1292
In 'Illuminate/Foundation/Application.php':registerCoreContainerAliases()
https://segmentfault.com/a/1190000004946198
Facades reference list:
https://learnku.com/docs/laravel/5.5/facades/1291
ServiceProvider binding:
https://learnku.com/docs/laravel/5.5/container/1289
Vue.js nuxt: https://zh.nuxtjs.org/guide/installation
Laravel-mix webpack.mix.js
example:
require('dotenv').config();
let mix = require('laravel-mix');
mix
.js('resources/assets/js/app.js', 'public/js')
.styles([
'node_modules/open-sans-all/css/open-sans.css',
'node_modules/font-awesome/css/font-awesome.css',
'resources/assets/css/style.css'
], 'public/css/style.css')
.copy('node_modules/open-sans-all/fonts', 'public/fonts')
.copy('node_modules/font-awesome/fonts', 'public/fonts')
.copy('resources/assets/images', 'public/images')
.browserSync({
proxy: process.env.APP_URL,
open: false
})
;
// config webpack to extract vue.js SFCs' CSS to public/css/vue-style.css:
mix.options({
extractVueStyles: 'public/css/vue-style.css'
});
mix.webpackConfig({
resolve: {
alias: {
'vue$': 'vue/dist/vue.runtime.esm.js'
}
}
});
package.json
example (using Vue.js):
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --display-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.17",
"browser-sync": "^2.18.13",
"browser-sync-webpack-plugin": "^1.2.0",
"core-js": "^2.5.1",
"cross-env": "^5.1",
"dotenv": "^4.0.0",
"font-awesome": "^4.7.0",
"laravel-mix": "^1.4",
"open-sans-all": "^0.1.2",
"vue": "^2.5.3"
}
Laravel blade template system interpolation V.S. Vue.js interpolation:
Use {!! !!} syntax from Laravel blade template system instead of original {{ }} syntax, so that the following script can work:
<script type="text/javascript">
window.vue_listing_model = "{!! addslashes(json_encode($model)) !!}"
</script>
Content distribution( in Vue.js) as content-child( content projection in Angular), is to solve the following problem, making components more reusable:

VUEX mechanism:
VUEX has a store with lots of states inside it. To change any state in the store, you should use mutators to do so, and mutators are synchronous so that race conditions are avoided when changing the states in the store. And use commit(mutation-name, payload) to invoke the mutation method inside the store to make the state change happen. If you have state gained from asynchronous resources as from invoking APIs, you have to use actions defined in your Vues.Store, in the action functions use commit(mutation-name, payload) as well to change the state. To invoke actions defined in the store, use this.$store.dispatch(action-name, payload) method.
composer resource:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
composer config -g --unset repos.packagist
composer config repo.packagist composer https://mirrors.aliyun.com/composer/
composer config --unset repos.packagist
composer -vvv require alibabacloud/sdk
composer create-project laravel/laravel=5.6.* –prefer-dist hokhykblog
composer require barryvdh/laravel-ide-helper –dev
composer require prettus/l5-repository
php artisan vendor:publish --provider "Prettus\Repository\Providers\RepositoryServiceProvider"
composer require league/fractal
Or use resource API classes:
php artisan make:resource User
php artisan make:resource Users --collection
php artisan make:resource UserCollection
TDD:
php artisan make:entity "Blog\Article"
php artisan make:factory ArticleFactory
php artisan make:factory PostFactory --model=Post
Model schema serialization using casts
array:
in Model: protected $casts = ['saved' => 'array'];
in Migration file: $table->text('saved');
$php artisan tinker
echo gettype($model->saved())
//array
in Seeder file: EloquentModel::create([..., 'saved'=>[1,2,3,4], ...]);
class MyTest extends PHPUnit_Framework_TestCase{
/**
* @group specification
*/
public function testSomething(){
}
phpunit --group specification
php artisan make:test NewPostTest
php artisan make:test NewPostTest --unit
public function testUserLoginsSuccessfully()
{
$user = factory(User::class)->create([
'email' => '[email protected]',
'password' => bcrypt('toptal123'),
]);
$payload = ['email' => '[email protected]', 'password' => 'toptal123'];
$this->json('POST', 'api/login', $payload)
->assertStatus(200)
->assertJsonStructure([
'data' => [
'id',
'name',
'email',
'created_at',
'updated_at',
'api_token',
],
]);
}
public function testsRegistersSuccessfully()
{
$payload = [
'name' => 'John',
'email' => '[email protected]',
'password' => 'toptal123',
'password_confirmation' => 'toptal123',
];
$this->json('post', '/api/register', $payload)
->assertStatus(201)
->assertJsonStructure([
'data' => [
'id',
'name',
'email',
'created_at',
'updated_at',
'api_token',
],
]);;
}
public function testsRequiresPasswordEmailAndName()
{
$this->json('post', '/api/register')
->assertStatus(422)
->assertJson([
'name' => ['The name field is required.'],
'email' => ['The email field is required.'],
'password' => ['The password field is required.'],
]);
}
public function testsRequirePasswordConfirmation()
{
$payload = [
'name' => 'John',
'email' => '[email protected]',
'password' => 'toptal123',
];
$this->json('post', '/api/register', $payload)
->assertStatus(422)
->assertJson([
'password' => ['The password confirmation does not match.'],
]);
}
public function testUserIsLoggedOutProperly()
{
$user = factory(User::class)->create(['email' => '[email protected]']);
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$this->json('get', '/api/articles', [], $headers)->assertStatus(200);
$this->json('post', '/api/logout', [], $headers)->assertStatus(200);
$user = User::find($user->id);
$this->assertEquals(null, $user->api_token);
}
public function testUserWithNullToken()
{
// Simulating login
$user = factory(User::class)->create(['email' => '[email protected]']);
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
// Simulating logout
$user->api_token = null;
$user->save();
$this->json('get', '/api/articles', [], $headers)->assertStatus(401);
}
public function testsArticlesAreCreatedCorrectly()
{
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$payload = [
'title' => 'Lorem',
'body' => 'Ipsum',
];
$this->json('POST', '/api/articles', $payload, $headers)
->assertStatus(200)
->assertJson(['id' => 1, 'title' => 'Lorem', 'body' => 'Ipsum']);
}
public function testsArticlesAreUpdatedCorrectly()
{
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$article = factory(Article::class)->create([
'title' => 'First Article',
'body' => 'First Body',
]);
$payload = [
'title' => 'Lorem',
'body' => 'Ipsum',
];
$response = $this->json('PUT', '/api/articles/' . $article->id, $payload, $headers)
->assertStatus(200)
->assertJson([
'id' => 1,
'title' => 'Lorem',
'body' => 'Ipsum'
]);
}
public function testsArtilcesAreDeletedCorrectly()
{
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$article = factory(Article::class)->create([
'title' => 'First Article',
'body' => 'First Body',
]);
$this->json('DELETE', '/api/articles/' . $article->id, [], $headers)
->assertStatus(204);
}
public function testArticlesAreListedCorrectly()
{
factory(Article::class)->create([
'title' => 'First Article',
'body' => 'First Body'
]);
factory(Article::class)->create([
'title' => 'Second Article',
'body' => 'Second Body'
]);
$user = factory(User::class)->create();
$token = $user->generateToken();
$headers = ['Authorization' => "Bearer $token"];
$response = $this->json('GET', '/api/articles', [], $headers)
->assertStatus(200)
->assertJson([
[ 'title' => 'First Article', 'body' => 'First Body' ],
[ 'title' => 'Second Article', 'body' => 'Second Body' ]
])
->assertJsonStructure([
'*' => ['id', 'body', 'title', 'created_at', 'updated_at'],
]);
}
public function testInvalidPostID() {
$this->json('PUT', '/posts/2222/sh-comments/1001', [
'input' => 'SomeThing'
], [
'Authorization' => 'Bearer ' . app('jwt')->getTokenForUser(2)
])->dump() //dumpnig output as array
->seeJsonStructure(['errors' => [
'*' => [
'message'
]
]
])->assertResponseStatus(404);
}
php artisan make:Model Article -m (--migration)
php artisan make:migration --table=users adds_api_token_to_users_table
php artisan migrate --step
php artisan make:seeder ArticlesTableSeeder
php artisan db:seed --class=ArticlesTableSeeder
php artisan make:controller ArticleController
Route::get('articles', 'ArticleController@index');
Route::get('articles/{id}', 'ArticleController@show');
Route::post('articles', 'ArticleController@store');
Route::put('articles/{id}', 'ArticleController@update');
Route::delete('articles/{id}', 'ArticleController@delete');
public function updateArticleCategory(UpdateArticleCategoryRequest $request)
{
$articleCategory = $this->call(UpdateArticleCategoryAction::class, [$request]);
$data = $this->transform($articleCategory, ArticleCategoryTransformer::class);
return $this->json(['code' => Response::HTTP_OK, 'message' => '编辑成功', 'result' => $data]);
}
public function deleteArticleCategory(DeleteArticleCategoryRequest $request)
{
$this->call(DeleteArticleCategoryAction::class, [$request]);
return $this->json(['code' => Response::HTTP_OK, 'message' => '删除成功']);
}
Passport Oauth2:
php artisan passport:client --password
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => '[email protected]',
'password' => 'my-password',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);
public function testServerCreation()
{
Passport::actingAs(
factory(User::class)->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(200);
}
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Laravel\Passport\Events\AccessTokenCreated' => [
'App\Listeners\RevokeOldTokens',
],
'Laravel\Passport\Events\RefreshTokenCreated' => [
'App\Listeners\PruneOldTokens',
],
];
use Illuminate\Http\Request;
Route::get('/orders', function (Request $request) {
if ($request->user()->tokenCan('place-orders')) {
//
}
});
检查作用域
Passport 提供两个可用于验证输入请求是否经过已发放作用域的令牌认证的中间件。开始之前,添加下面的中间件到 app/Http/Kernel.php 文件的 $routeMiddleware 属性:
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
Git commands:
git init
git remote add ....
git push --set-upstream origin master
git checkout -b dev
git push origin dev:dev //git push origin :dev --to delete the branch
git push --set-upstream origin dev
git checkout --track origin/dev //create local dev branch to track existing remote branch or use the following:
git checkout -b dev origin/dev
1.owner在远程库更新了分支,但是在我这里git branch -a查看不到新的分支,
解决办法:git fetch origin --prune 更新一下分支信息,然后再git branch -a就能看到新的分支了~
2.多人协作的工作模式通常是这样:
首先,可以试图用git push origin <branch-name>推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!
5. 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>。
php artisan make:controller API/AuthController
php artisan make:resource ModelsResource
ARTISAN:
Available commands:
clear-compiled Remove the compiled class file
down Put the application into maintenance mode
env Display the current framework environment
help Displays help for a command
inspire Display an inspiring quote
list Lists commands
migrate Run the database migrations
optimize Optimize the framework for better performance
serve Serve the application on the PHP development server
tinker Interact with your application
up Bring the application out of maintenance mode
app
app:name Set the application namespace
auth
auth:clear-resets Flush expired password reset tokens
cache
cache:clear Flush the application cache
cache:table Create a migration for the cache database table
config
config:cache Create a cache file for faster configuration loading
config:clear Remove the configuration cache file
db
db:seed Seed the database with records
event
event:generate Generate the missing events and listeners based on registration
handler
handler:command Create a new command handler class
handler:event Create a new event handler class
key
key:generate Set the application key
make
make:command Create a new command class
make:console Create a new Artisan command
make:controller Create a new resource controller class
make:event Create a new event class
make:job Create a new job class
make:listener Create a new event listener class
make:middleware Create a new middleware class
make:migration Create a new migration file
make:model Create a new Eloquent model class
make:policy Create a new policy class
make:provider Create a new service provider class
make:request Create a new form request class
make:seeder Create a new seeder class
make:test Create a new test class
migrate
migrate:install Create the migration repository
migrate:refresh Reset and re-run all migrations
migrate:reset Rollback all database migrations
migrate:rollback Rollback the last database migration
migrate:status Show the status of each migration
queue
queue:failed List all of the failed queue jobs
queue:failed-table Create a migration for the failed queue jobs database table
queue:flush Flush all of the failed queue jobs
queue:forget Delete a failed queue job
queue:listen Listen to a given queue
queue:restart Restart queue worker daemons after their current job
queue:retry Retry a failed queue job
queue:subscribe Subscribe a URL to an Iron.io push queue
queue:table Create a migration for the queue jobs database table
queue:work Process the next job on a queue
route
route:cache Create a route cache file for faster route registration
route:clear Remove the route cache file
route:list List all registered routes
schedule
schedule:run Run the scheduled commands
session
session:table Create a migration for the session database table
vendor
vendor:publish Publish any publishable assets from vendor packages
view
view:clear Clear all compiled view files
npm install --registry=https://registry.npm.taobao.org
npm install –save-dev @angular/cli@latest
npm uninstall bootstrap-sass jquery lodash –save-dev
ng new Client --style=scss --routing
ng add @angular/pwa
ng generate module pages/home –routing
ng generate component pages/home
ng generate interface interface/user
ng g s services/dataService –module=app
ng g service pages/auth/_services/auth
ng g service shared/_services/http-interceptor
ng g class pages/auth/user
ng g pipe sex-reform
ng build –prod
ng build –prod –href-base /url in browsser
$ng g library zero –prefix hok
$ npm install --save @ngrx/effects @ngrx/store
$ npm install --save-dev @ngrx/schematics
$ npm install –save-dev @ngrx/store-devtools
$ ng generate @ngrx/schematics:store State --root --module app.module.ts
$ ng g @ngrx/schematics:reducer ZipCodes –group
$ ng g @ngrx/schematics:action Zipcode –group
$ ng g @ngrx/schematics:effect Zipcode –group
4 different route guards:
CanActivate, CanActivateChild, CanDeactivate, CanLoad
$ ng g guard pages/auth/_guards/auth
$ npm install –save-dev @ng-bootstrap/schematics
$npm install –save @ng-bootstrap/[email protected] for angular8
To integrate bootstrap scss code into src/styles.scss:
@import "../node_modules/bootstrap/scss/functions";
@import "../scss/bootstrap/_variables.scss";
@import "../node_modules/bootstrap/scss/_variables.scss";
@import "../node_modules/bootstrap/scss/mixins";
@import "../node_modules/bootstrap/scss/root";
@import "../node_modules/bootstrap/scss/reboot";
@import "../node_modules/bootstrap/scss/type";
@import "../node_modules/bootstrap/scss/images";
@import "../node_modules/bootstrap/scss/code";
@import "../node_modules/bootstrap/scss/grid";
@import "../node_modules/bootstrap/scss/tables";
@import "../node_modules/bootstrap/scss/forms";
@import "../node_modules/bootstrap/scss/buttons";
@import "../node_modules/bootstrap/scss/transitions";
@import "../node_modules/bootstrap/scss/dropdown";
@import "../node_modules/bootstrap/scss/button-group";
@import "../node_modules/bootstrap/scss/input-group";
@import "../node_modules/bootstrap/scss/custom-forms";
@import "../node_modules/bootstrap/scss/nav";
@import "../node_modules/bootstrap/scss/navbar";
@import "../node_modules/bootstrap/scss/card";
@import "../node_modules/bootstrap/scss/breadcrumb";
@import "../node_modules/bootstrap/scss/pagination";
@import "../node_modules/bootstrap/scss/badge";
@import "../node_modules/bootstrap/scss/jumbotron";
@import "../node_modules/bootstrap/scss/alert";
@import "../node_modules/bootstrap/scss/progress";
@import "../node_modules/bootstrap/scss/media";
@import "../node_modules/bootstrap/scss/list-group";
@import "../node_modules/bootstrap/scss/close";
@import "../node_modules/bootstrap/scss/modal";
@import "../node_modules/bootstrap/scss/tooltip";
@import "../node_modules/bootstrap/scss/popover";
@import "../node_modules/bootstrap/scss/carousel";
@import "../node_modules/bootstrap/scss/utilities";
@import "../node_modules/bootstrap/scss/print";
Notice that the file import sequence of _variables.scss
:
@import "../scss/bootstrap/_variables.scss";
@import "../node_modules/bootstrap/scss/_variables.scss";
Because variables in Bootstrap4 are usually defined with !default
keyword, thus self-defined variables are imported before Bootstrap variables to override variables’ default values.
Making 3rd party library of your own:
$ng g library zero --prefix hok //// 创建第三方库 (--prefix: 前缀;在用命令行新建组件/指令时,selector的属性值的前缀)
$cd projects/zero/src/lib // 切换至第三方库创建组件的位置
$ng g c button //创建 button component
// zero.module.ts
exports: [ButtonComponent]
// 切换至第三方包根目录下
$ cd projects/zero
// 构建
$ ng build zero
// 需要登录(npm login),有自己的npm账号
$ npm publish
$ npm install zero@latest --save
imports: [ZeroModule]
<hok-button></hok-buttom>
Stylelint configuration:
npm install stylelint --save-dev
npm install stylelint-config-standard --save-dev
npm install stylelint-scss --save-dev
// https://github.com/stylelint/stylelint
npm install tslint-angular --save-dev
// https://angular.io/guide/styleguide
In package.json scripts
section:
"lint": "ng lint",
"lint:dev": "npm run sasslint && npm run tslint",
"sasslint": "./node_modules/.bin/stylelint \"src/**/*.scss\" --syntax scss || echo \"Ops: Stylelint faild for some file(s).\"",
"tslint": "./node_modules/.bin/tslint --project tsconfig.json || echo \"Ops: TSlint faild for some file(s).\"",
Copying .stylelintrc
file to the root folder of your project:
{
"extends": ["stylelint-config-standard"],
"rules": {
"font-family-name-quotes": "always-where-recommended",
"function-url-quotes": [
"always",
{
"except": ["empty"]
}
],
"selector-attribute-quotes": "always",
"string-quotes": "double",
"max-nesting-depth": 3,
"selector-max-compound-selectors": 3,
"selector-max-specificity": "0,3,2",
"declaration-no-important": true,
"at-rule-no-vendor-prefix": true,
"media-feature-name-no-vendor-prefix": true,
"property-no-vendor-prefix": true,
"selector-no-vendor-prefix": true,
"value-no-vendor-prefix": true,
"no-empty-source": null,
"selector-class-pattern": "[a-z-]+",
"selector-id-pattern": "[a-z-]+",
"selector-max-id": 0,
"selector-no-qualifying-type": true,
"selector-max-universal": 0,
"selector-pseudo-element-no-unknown": [
true,
{
"ignorePseudoElements": ["ng-deep"]
}
],
"unit-whitelist": ["px", "%", "em", "rem", "vw", "vh", "deg"],
"max-empty-lines": 2
}
}
tslint.json
:
{
"extends": ["../tslint.json", "../node_modules/tslint-angular"],
"rules": {
"angular-whitespace": [true, "check-interpolation", "check-semicolon"],
"no-unused-variable": true,
"no-unused-css": true,
"banana-in-box": true,
"use-view-encapsulation": true,
"contextual-life-cycle": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
Using Redis in Laravel:
laravel 如何使用sorted sets?跟前一篇文章差不多。連到某一篇article頁面後,把key (trending_articles) 當中的成員,也就是這篇文章的id,分數加1。
Route::get('articles/{article}', function (App\Article $article) {
Redis::zincrby('trending_articles', 1, $article->id);
return $article;
});
articles/trending 頁面顯示熱門前3名的文章。
$trending = Redis::zrevrange('trending_articles', 0, 2);
return $trending;
sorted sets 的key或成員並不需要先用zadd加入,使用zincrby也能直接加產生資料。
成員以id來記錄,有時不是那麼容易看出來是什麼東東?也可以把整個model作為成員。laravel會把model轉成json格式再儲存。
Redis::zincrby('trending_articles', 1, $article);
// 以下語法等同上面
Redis::zincrby('trending_articles', 1, (string)$article);
Redis::zincrby('trending_articles', 1, $article->toJson());
但重點轉成json格式後,在怎麼再轉成看得懂的code?
$trending = App\Article::hydrate(
array_map('json_decode', $trending)
);
這樣就會解出排名好的collection。
如果要移除成員,可以使用Redis::zremrangebyrank(‘trending_articles’, 0, -4),移除第四名以後的成員。建議寫成cron job定期刪除。