php学习(二):php的web框架,最优雅的Laravel框架

php   8.1.0            (php -v)

laravel 安装器   4.2.9

laravel  8.74.0       (php artisan --version)

Django vs Laravel:谁才是更好的Web开发框架 - 闪电博

1.Laravel安装

1.安装composer包管理器
先将php.exe路径添加到 系统的环境变量

1.通过Composer-setup.exe安装(建议使用)

 将安装目录指向php.exe文件就行
 
 下载目录: https://docs.phpcomposer.com/00-intro.html#Downloading-the-Composer-Executable

2.通过在php文件目录下添加Composer.phar启动文件的方式安装

(1.下载Composer.phar (composer 中文网)
http://composer.p2hp.com/download/

(2.composer.bat文件
@php "%~dp0composer.phar" %*

(3.在cmd管理员模式下运行composer

3.设置为阿里云镜像 (全局)

阿里云镜像: https://developer.aliyun.com/composer

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
2.配置php环境(8.x.x)

在php.exe同级目录下,存在一个php.ini文件, 打开php.ini扩展配置, 进行以下的配置


1.修改 extension_dir = "./"  扩展路径,开启路径扩展

    注释 ;extension_dir = "./"
    添加 extension_dir = "ext"

2.Laravel必要开启的 **openssl、mbstring和fileinfo** 3个扩展

​       extension=fileinfo

​      extension=mbstring

​       extension=openssl   去除封号

3.其它扩展  **curl 、pdo_mysql
​        extension=curl

​       extension=pdo_mysql 去除封号

3.创建Laravel项目

1.通过composer创建

## 使用composer 创建一个为laravel-demo  名称的laravel项目
composer create-project --prefer-dist  laravel/laravel  BlogServe

## (进入项目后),安装依赖中的包(出现了vendor)
composer install --ignore-platform-reqs
 
##  启动Laravel 
php artisan serve  (--port = 8888)    默认8000端口

其它安装也可以使用composer require laravel/jetstream --ignore-platform-reqs (强制安装包,可能是php版本太高)

2.通过laravel安装器创建(建议使用)

相当于vue的@vue/cli脚手架

composer global require "laravel/installer"           全局安装laravel安装器

laravel new  BlogServe                                创建BlogServe项目

php artisan serve  (--port=8888)                  ##  启动Laravel

3.Laravel目录文件

**app**:               应用的核心代码
**bootstrap**:   少许文件,用于框擘的启动和自动载入百置
**config**:          应用的所有配活文件
**database**;    包含数规库迁移文件和境充文件
**public**:          应用的入口文件,前端资源文件:图片,js,cSs
**resources**:   视图文件资源
**routes**:         应用定义的所有路由
**storage**:       编译后blade模板,session,文件缓存
**tests**:            自动化测试文件
**vendor**:       第三方类库,通过composer加载的依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMSB7ymH-1660871673249)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211211155211371.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nE58SrVU-1660871673251)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211211155514894.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pNk2IbM-1660871673252)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211211155548907.png)]

composer.json文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCTkYadP-1660871673253)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211211155716416.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TbJ9dlJw-1660871673255)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211211155734539.png)]

4.运行Git仓库项目

**1.composer无法安装依赖包 **

注意:如果出现composer update 无法更新包的情况, “Your requirements could not be resolved to an installable set of packages.” 这是composer版本引起的问题,然后安装成功后就会生成vendor 文件夹

--在项目下执行以下命令:
composer update --ignore-platform-reqs             更新Composer,(包的依赖)忽略平台问题
composer install --ignore-platform-reqs            安装项目模块, 忽略平台问题

2.设置应用密钥(同平台,则非必要)

注意: 
   如果访问接口时,出现  {   "message": "Server Error" }, 并且是500报错, 或者出现 The payload is invalid.  则都是加密出现问题!
   
   查看storage/logs/laravel.log 文件  (先清空所有日志,再访问接口,查看新的日志.),如果出现报错:  No application encryption key has been specified, 则出现此问题的原因是:没有设置应用程序加密密钥。
   
   这行命令会导致crypt加密失效(密码加密),所以原来在mysql数据库的数据也无法正常编码和解码。  这告诉我们: 1.不使用crypt方式加密密码,  2.不使用原来sql文件的用户注册数据 
   
   
--在项目下执行以下命令:
php artisan key:generate               设置应用密钥

结果: 设置成功后会在根目录的.env中有显示:APP_KEY的新值

3.配置.env文件

拉取一个项目,一定需要.env文件才能正常访问(访问数据库出现了500错误)。

但正常的项目.env文件是不会被上传到gitee中的。.gitignore文件中的.env文件默认不上传.

所以在上传到gitee中,一定要去除注释.gitignore文件中的.env文件,不然该文件不会被保存。

4.配置mysql数据库

1. 修改.env文件的配置

2. 修改config/database.php文件的配置(这是默认数据库配置)

2.Laravel接口学习

1.routes路由

routes文件中定义路由规则 web.php

Route::get('/', function () {
    return view('welcome');
});

Route::get('user/add', function () {
    return view('user/add');          // 可以直接访问 resources中的views,
                                      // 返回一个模板文件 html
});

//
//Route::get('user/add',"UserController@add");
Route::post('user/store',"UserController@store");   // 给这个Url绑定一个控制器


// 后台登录路由
Route::get('admin/login','Admin\LoginController@login',['middleware'=>'cors']);

模板文件中(xx.blade.php)
可以使用{{ url('admin/code')}}  直接访问url路由, 而Django中使用 {% url xxxxx %}  , url上面的别名来访问自定义路由。


分组路由
Route::group(['prefix' => 'auth','namespace'=>'Admin','middleware'=>'cors'], function(){
    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('me', 'AuthController@me');
});
2.resources模板文件

resources/views中放置模板文件 views/user/add.blade.php

注意:模板文件以 .blade.php结尾

views文件中添加模板页面

表单元素在头部应当添加 一个特殊csrf字段


{{ csrf_field()  }} 
或者 <input type="hidden" name="_token" value="{{ csrf_token() }}">

有时使用异步请求,则在head中添加 meta
<meta name="csrf-token" content=" {{ csrf_token() }}">
       
        

引入样式 一般是放在public文件夹

但依旧引入不了 resources中的css文件。


{{--<link rel="stylesheet" href="{{URL::asset('css/1.css')}}">--}}
<link rel="stylesheet" href="/css/1.css">  

redirect 重定向


return  redirect('admin/auth/logout') ;

public目录下,属于可以直接访问的静态资源


http://localhost:8001/assets/home/swiper/bg-swiper1.jpg

3.Controllers控制器*

1.创建基础的控制器

app/Http/Controllers 控制器,逻辑控制,数据获取。

(1.开启路由服务

注意: 路由访问控制器时,应该打开app/Provides/RouteServiceProvider.php 中的路由服务

protected $namespace = 'App\\Http\\Controllers';

(2.创建基础控制器(可以复制, 但一定要注意默认的命名空间)

php artisan make:controller UserController
默认的控制器命名空间:

namespace App\Http\Controllers;  // 默认是这个命名空间

2.创建完整的控制器

php artisan make:controller Admin\UserController --resource

php artisan route:list 查看路由

<?php

namespace App\Http\Controllers;  //  引入控制器

use Illuminate\Http\Request;     //  引入控制器
use App\Models\User;             // 引入模型

class UserController extends Controller
{
    // 添加方法
    public function add(){
        return view("");
    }
    // 存储方法
    public function store(Request $request){       
// 获取请求中的所有数据相当于python的POST.dict()
//        $input = $request->all();             
//  数据过滤,获取除"_token”以外的数据
        $input = $request->except("_token");

        $input["password"] = md5($input["password"]);    // md5 数据加密

//     User::create(["username"=>$input['username'],
"password"=>$input['password']]);  // 按需求添加数据
        $res = User::create($input);        // 添加数据


        // 1.如果添加成功,跳转到列表页, 否则 ,跳转回原页面
        if($res){
            return redirect("user/index");  // 跳转到目标页
        }else{
            return back();
        }
//       dd($res);            // 直接输出
//       echo "hello";
    }
    
    public function index(){
       ....
    }
}
//  用户模块相关路由
Route::resource('user','UserController');
// admin/user  和POST  可以访问 UserController中的store方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JWZpNuLX-1660871673257)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211212172216664.png)]

RouteServiceProvider.php附录

<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * The path to the "home" route for your application.
     *
     * This is used by Laravel authentication to redirect users after login.
     *
     * @var string
     */
    public const HOME = '/home';

    /**
     * The controller namespace for the application.
     *
     * When present, controller route declarations will automatically be prefixed with this namespace.
     *
     * @var string|null
     */
    protected $namespace = 'App\\Http\\Controllers';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));
        });
    }

    /**
     * Configure the rate limiters for the application.
     *
     * @return void
     */
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
        });
    }
}

4.Models 模型

app/Http/Models 模型,与数据库进行交互 User.php

命令行创建模型: php artisan make:model User

(也可以为Model\User, 如果 没有Model文件夹的情况下。 其实在php中查找路径下是使用反斜杠,这和C语言很类似,但python却是正斜杠)

这里注意创建 数据库的时候,id值设置为自动增长。

python的Django是不用设置id的,因为它的数据表由Django自动创建。id会自动设置为主键。

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;



    protected $table = 'blog_user';     // 1.用户模型关联表
    protected $primaryKey = 'id';        //2. 关联表的主键

    protected $fillable = [              // 3.允许被批量操作的字段, 相当于python的一个Meta中的序列器
        'username',
        'password',
    ];
    public $timestamps = false;         // 4.禁用时间戳

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}
5.MySQL数据库连接
1.database.php数据库

在当前文件中配置mysql基本配置, config/database.php

'mysql' => [
    'driver' => 'mysql',                        //数据库的类型
    'host' => env('DB_HOST', 'localhost'),      //数据库的位置
    'port' => env('DB_PORT', '3306'),           //端口号
    'database' => env('DB_DATABASE', 'laravel-test'),  //数据库名
    'username' => env('DB_USERNAME', 'root'),  //用户名
    'password' => env('DB_PASSWORD', '123456'),       //密码
    'charset' => 'gb2312',                        //字符集
    'collation' => 'gb2312_chinese_ci',           //排序方式
    'prefix' => '',                             //前缀
    'strict' => true,                           //Strict模式
    'engine' => null,                           //引擎
],
2…env系统环境变量

.env是框架的环境变量,是为了让这个选项在不同环境下有不同的值。
.env文件在laravel根目录下。

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=laravel-test
DB_USERNAME=root
DB_PASSWORD=123456

如果是导入mysql文件,则不需要执行以下内容。

3.生成迁移文件(可省)

执行命令

php artisan make:migration create_user_table

这样我们就可以在database/migrations目录下发现我们新生成的文件。

xxxx_create_user_table.php

修改文件,创建新表

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUserTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        //这里是新增消息(表,列,索引)的位置
        
        Schema::create('user', function (Blueprint $table) {    //建立数据表user
            $table->increments('id');               //主键自增
            $table->string('name')->unique();       //'name'列唯一
            $table->string('password');             //'password'
            $table->string('email')->unique();      //'email'唯一
            $table->timestamps();                   //自动生成时间戳记录创建更新时间
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //这里删除信息的位置
    }
}

4.执行迁移(可省)
php artisan migrate

执行成功后,即可在MySQL数据库中查看新生成的表

6.data获取数据
1.Form表单中获取数据
1.带请求参数
function login(Request $request){  
     // (1.数据过滤,获取除"_token”以外的数据,(数组形式获取)
    $input = $request->all();          
    // 获取请求中的所有数据相当于python的POST.dict() 
     // (2.数据过滤,获取除"_token”以外的数据, 参数可以为字符串或者数组字符串
    $input = $request->except("_token");   
    $input = $request->except(["_token"]);   
     //(3.获取仅有的两个参数
     $request->only('username', 'password')
     $request->only(['username', 'password'])
     //(4. 获取查询字符串, 存在则返回目标数据,返回则为null
     $request->query('name', null);
     // 类似于 request.get('name',None)
     //(5.获取指定的用户输入数据 ,可以为任何的方法(get /post/ ..)
     $name = $request->input('name');
     $name = $request->input('name', null); 
    // (6.获取文件数据
    $temp=$request->file('file');     
     
     // 请求头数据, 返回的是数组
    $input = $request->header();  //返回请求头header信息 
    $input = $request->cookie();  //返回cookie信息    
    $input = $request->session(); //返回session信息
    $input = $request->path();   //返回请求路径信息 
    $input = $request->url();    //返回不带请求参数的url地址  
    $input = $request->method(); //返回控制器的操作方法信息
    
    
//   php不允许请求头上的字段带有下划线的数据_,因为它会默认转换为-
//   php不允许请求头上的字段带有驼峰习惯的数据,因为它会默认转换为小写
     $accesstoken = json_encode( $request->header()['access-token'][0]);
     $accesstoken = str_replace("bearer","",$accesstoken);
     echo $accesstoken;
     
    // 获取请求的路由
    $res = $request->path()    
    echo $res    
     
  }

2.使用默认的参数request (Jwt专属)
 public function login(){
       $credentials = request(['username', 'password']);
       
 }
 
2.Mysql数据库数据处理

1.基础数据查询

 1.插入数据 create  
  $res = User::create($input);        // 添加数据
  $res = User::create(["username"=>$input['username'],"password"=>$input['password']]);   
  注意:设计的表,需要有 "自增id" .
  
  
2.查询数据select
2.1 get()/ find() / first()  (query方法可以省略)
 // 获取所有数据
  $user = User::get();               
  $user = User::find($id);         
  $user = User::query()->where('id', $id)->first();                
  // 查询指定用户数据(首个信息)
  $user = User::where("user_name",$input['username'])->first();     
  // 查询指定用户数据(首个信息)
   // 相当于 User.objects.filter(nickname=nickname, sex=1).first ()   
  $user = User::where("user_name",$input['username'])->get();      
  // 查询指定用户数据(所有信息)
2.2 toArray转换数据格式方法(需要先查询数据)
   // 将获取的对象数组(模型对象),转换成php数组,
   // 转换的结果只可以使用[] 访问
   // php 直接遍历数据集,使用 -> 访问属性,但也可以使用 []访问属性
   // python 直接遍历数据集,也是使用 .访问属性,而不是使用[]访问属性
   $item = SceneMsg::where('type_id',$type_id)->get()->toArray(); 
2.3 makeVisible临时、暴露隐藏字段
    $user->makeVisible('attribute')->toArray(); 
    $user->makeHidden('attribute')->toArray(); 
2.4 distinct、count去重计数(distinct只有配合count,select使用)
     $usercomment = UserComment::where('user_id',$userinfo['id'])->distinct('user_id')->count('id');
     $department_list = User::select('department')->distinct("department")->get();
2.5.inRandomOrder() 随机获取表中数据
    $sceneinfo = SceneMsg::inRandomOrder() ->take(2) ->get();
    sql内容:SELECT * FROM table ORDER BY RAND() LIMIT 100
2.6 take()获取指定数量的数据
    $sceneinfo = SceneMsg::inRandomOrder() ->take(2) ->get();

 
3.更新数据 update
  $res = User::where("user_name",$input['username'])->update(['token'=> $token]]);  
  // 更新数据, 更新数据必须是 "数组形式" 。
  
  
4.删除数据  delete
  $res = User::where("user_name",$input['username'])->delete();         
  // 删除数据
 

2.过滤数据的方法

1. 模糊查询, where + like  +  '%'  / '_'
  $category1_arr = SceneActivity::where('post_type','like',$category1.'%')->get(); 
    // where + like  %模糊查询
  $category3_arr = SceneActivity::where('post_type','like',$category3.'_')->get(); 
  // where + like  _单字符模糊查询
 
 
  
2.数据排序, orderBy (ASC 升序, DESC 降序), take限制数量
   $item = SceneMsg::where('excluded_period_start', '<', $start)
          ->where('excluded_period_end', '>=', $end)   // 不需要日期格式,字符串就可以 
          ->where('active', 1)
          ->orderBy('email', 'ASC')                    // 按照email  ASC升序 , DESC降序 
          ->take(10)
          ->paginate(10)                                // where + 过滤日期数据
 
 
 
3.条件查询 where 
3.1.where  ... orWhere  或查询 (是否) 
    // where + orWhere + like 模糊查询    
    $user = User::where("user_name",$input['username']) -> orWhere('title','like','%'.$echostr.'%') ->get() 3.2whereNotIn方法(不等于)
    User::whereNotIn('user_id', [1, 2]); 
sql内容: SELECT * FROM `user` where `user_id`  Not IN (1, 2) 
  
  
 
4.子查询,使用whereIn  + 自带闭包 + select... from    + orWhereIn 
User::whereIn('id', function($query){ 
    $query->select('user_id')->from('admin_user')
         ->whereIn('type', ['1', '2']); 
})->get(); 
sql内容: SELECT * FROM `user` where `id` IN ( SELECT `user_id` FROM `admin_user` WHERE `type` IN (1, 2) );
  
   
4.1.子查询,传递参数 use(xxx)
$candidate = CollegeCandidate::whereIn('college_id',function($query)use($student){
     $query ->select('id')->from('college-type-info')
      ->where('college_name',$student['school_name']);
})->get()->toArray();
  

 
5多表连接   ORM   (select +  Join)// DB::table  / 
优点:没有二级数组,不会存在空值取数据的情况。 
use DB;                                            引入DB数据库
public $SceneMsgTable = 'scene-msg';               定义数据库名
public $SceneTypeTable = 'scene-type';
// 多表连接的方法,加快访问速度, 其中 DB::table($this->SceneMsgTable) 和 SceneMsg等价
$scene_arr = SceneMsg::select(
         $this->SceneTypeTable.'.type_name'              // 后面的会覆盖前面的字段。建议主表放在最后面。
         $this->SceneMsgTable.'.*',
        )
    ->join(
        $this->SceneTypeTable,                         // leftJoin 和 join 用法相同,leftjoin左侧是不重要字段
        $this->SceneMsgTable.'.type_id',               //   '=',    '等号可以省略'
        $this->SceneTypeTable.'.id'
        )
    ->orderBy('create_time','DESC')
    ->get(); 
// 注意: 
// 1.ORM继承了原生DB大部分方法
// 2.如果使用ORM返回的数据集,则可以正常使用->  和 [] 获取属性值
// 3.如果使用原生DB 返回的数据集,不能使用[],对数据进行获取, 需要使用专有的 “->” ,并且控制器中也需要使用->

 


3.数据库的注意事项

  
1.获取当前的时间
 //将date函数默认时间设置中国区时间
 date_default_timezone_set('PRC');    
 $input["registerTime"] =  date('Y-m-d h:i:s', time()); 
 // 指定日期字符串转换成日期格式(H 是 24小时制, h是12小时制)
 $new_date = date('Y-m-d H:i:s', strtotime("2000/01/01")); 



2.从数据库获取的对象, “->” 相当于获取对象中的属性
  if(!$input['password'] != Crypt::decrypt($user->password) ){
  	  return array(xxxx);
  }
 
 
 
3. xxx->get()  返回的是对象数组, 与php的二级数组不同,(二维数组)
  并不能使用array_merge(xx,yy) 进行合并 , 这个主要是合并 对象使用的(也就是二维数组)
  默认返回模型对象, 只有all方法, toArray方法可以转换成普通的php数组类型。
  

4.跨数据库连表 (?)

//这里只需要在env里设置另一个数据库名字CRM_DB_DATABASE就行了(俩数据库都在一个server)
//code表 card表在coupon数据库;shop表在crm数据库,两个数据库在同一个server。
$db_crm = env('CRM_DB_DATABASE', 'crm');
$model = Code::select('code.*', 'shop.title')->leftJoin('card', 'card.id','code.card_id')
    ->leftJoin($db_crm . '.shop', 'shop.id', 'card.shop_id')
    ->get()->toArray();

5.自动触发的方法(???)

laravel8 模型监听之观察者监听事件

1.beginTransaction事务的使用
 DB::beginTransaction();
 try {
     WechatMenu::where('pid', $id)->delete();
     WechatMenu::where('id', $id)->delete();
     DB::commit();
 } catch (QueryException $exception) {
     DB::rollback();
     return redirect(route("wechat.menu"))->with('status', 'success')->withErrors(["操作失败!"]);
 }
    return redirect(route("wechat.menu"))->with('status', 'success')
 ->withErrors(["操作成功!"]);



2.DB 使用unprepared 方法执行Sql触发器(未知) 
public function up()
{
  DB::unprepared(' 
  CREATE TRIGGER roll_no BEFORE INSERT ON `students` FOR EACH ROW 
  BEGIN 
  SET @roll_num = IFNULL((substring((
  	SELECT student_roll_no FROM students WHERE  class_code = NEW.class_code
    ORDER BY created_at DESC LIMIT 1),-2) + 1), `1`),
    
    NEW.student_roll_no = CONCAT(  YEAR(CURRENT_DATE),  NEW.class_code,
    IF (@roll_num < 10, CONCAT(`0`, @roll_num), @roll_num) 
  )
  END;
  ');
}



3.Observer模型观察者 (未知)
您不需要为模型事件创建迁移。 Laravel雄辩的事件具有多个事件,例如被检索,创建,创建,更新,更新,保存,保存,删除,删除,还原,恢复,您可以轻松地使用它们。

首先,您应该像这样为模型创建观察者

php artisan make:observer UserObserver --model = User

在UserObserver中,您可以收听任何喜欢的事件,例如:

class UserObserver
{
    /**
     * Handle the User "created" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Handle the User "updated" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function updated(User $user)
    {
        //
    }
}
之后,您应该注册您的观察者以在 app / providers / AppServiceProvider 引导方法中建模,例如:

public function boot()
{
     User::observe(UserObserver::class);
}
 




3.response响应数据(api)
1.输出字符串
    // json_encode数组转换成字符串
    $credentials = array('username'=>$input['username'], 'password'=>$input['password']);
    echo json_encode($credentials);


2.使用response()方法 (jwt专属) 
    return response()->json(['error' => 'Unauthorized'], 401); 


3.响应字符串数组array信息
    php没有对象与字典的说法,但它的字符串索引数组就是类似结构, {msg:$msg, data:$data,code:$code};

    $this->responseTool('登录成功',null,200, $token);
 
    public function responseTool($msg,  $data, $code, $token=null){
        if($token){
            return array('msg'=>$msg,'data'=>$data,'code'=>$code,'token'=>$token);
        }else{
            return array('msg'=>$msg,'data'=>$data,'code'=>$code);
        }
    }


4.DB数据库方法(略)
首先引入DB     use DB;

 
1. 内连接(等值连接, 多表连接)
查询构建器还可以用于编写基本的SQL“内连接”,你可以使用查询构建器实例上的join方法,传递给join方法的第一次参数是你需要连接到的表名,剩余的其它参数则是为连接指定的列约束,www.bojiesz.com 当然,正如你所看到的,你可以在单个查询中连接多张表:


$users = DB::table('users')

            ->join('contacts', 'users.id', '=', 'contacts.user_id')

            ->join('orders', 'users.id', '=', 'orders.user_id')

            ->select('users.*', 'contacts.phone', 'orders.price')

            ->get();

2. 外连接(左连接 / 右连接)

如果你是想要执行“左连接”而不是“内连接”,可以使用leftJoin方法。该方法和join方法的使用方法一样:

$users = DB::table('users')

            ->leftJoin('posts', 'users.id', '=', 'posts.user_id')

            ->get();

3. 联合查询(Union查询,无连接结合查询)

UNION运算符可以将两个或两个以上上SELECT语句的查询结果集合  “合并成一个结果集合”  显示,即执行联合查询。

查询构建器还提供了一条“联合”两个查询的快捷方式,比如,你要创建一个独立的查询,然后使用union方法将其和第二个查询进行联合

$first = DB::table('users')
            ->whereNull('first_name');

$users = DB::table('users')
            ->whereNull('last_name')
            ->union($first)     // 联合的意思,没有任何的连接关系,对数据进行拼接
            ->get(); 
            

4. 嵌套查询(子查询)

你还可以指定更多的高级连接子句,传递一个闭包到join方法作为该方法的第2个参数,该闭包将会返回允许你指定join子句约束的JoinClause对象

DB::table('users')
        ->join('contacts', function ($join) {
            $join->on('users.id', '=', 'contacts.user_id')->orOn(...);
        })
        ->get();

$insert = DB::insert('insert into city set name="安徽",state="init"');

增:返回bool

$delete = DB::delete('delete from city where id > 5');

删:返回删除的行数

$update = DB::update('update city set name = "上海" where id = 8');

改:返回更新的行数

$city = DB::select('select * from city');

查:返回数组

}

}

3、使用查询构造器实现CURD

(1)增:

$insert_bool = DB::table('city')->insert(['name' => '湖南', 'state' => 'init']);

插入一条数据返回bool值

$insert_id = DB::table('city')->insertGetId(['name' => '湖北', 'state' => 'init']);

插入一条数据返回插入的记录id

$insert_bool = DB::table('city')->insert([['name' => '北京', 'state' => 'init'], ['name' => '上海', 'state' => 'init']]);

插入多条数据返回bool值

(2)删:

$delete_row = DB::table('city')->where('id', 14)->delete();

删除一条记录,返回受影响的行数,1行

$delete_table = DB::table('city')->truncate();

清空一张表,不返回任何标示

(3)改:

$update_row = DB::table('city')->where('id', 20)->update(['name' => '日本']);

更新一条数据返回受影响的行数,1行

$update_row = DB::table('carousel')->increment('order_number');

让所有记录的order_number都自增1,返回受影响的行数,6行

$update_row = DB::table('carousel')->decrement('order_number');

让所有记录的order_number都自减1,返回受影响的行数,6行

$update_row = DB::table('carousel')->increment('order_number', 100);

让所有记录的order_number都自增100,返回受影响的行数,6行

$update_row = DB::table('carousel')->decrement('order_number', 100);

让所有记录的order_number都自减100,返回受影响的行数,6行

$update_row = DB::table('carousel')->where('id', 1)->increment('order_number', 200);

修改一条记录id为1的order_number字段自增200,返回受影响的行数,1行

$update_row = DB::table('carousel')->where('id', 1)->increment('order_number', 200, ['name' => '自增同时修改字段']);

修改一条记录id为1的order_number字段自增200,同时去修改其他字段,返回受影响的行数,1行

(4)查:

$select_rows = DB::table('city')->get();

get方法查询所有记录

$first_row = DB::table('carousel')->orderBy('id', 'asc')->first();

first方法查询排序后的第一条

$select_rows = DB::table('carousel')->where('id', '>=', 2)->get();

where方法查询符合单个条件的所有记录

$select_rows = DB::table('carousel')->whereRaw('id >= ? and order_number > ?', [1, 5])->get();

whereRaw方法查询符合多个条件的所有记录

$select_field = DB::table('carousel')->pluck('name');

pluck方法查询符合条件的每条记录的name字段

$select_field = DB::table('carousel')->lists('name');

$select_field = DB::table('carousel')->lists('name', 'id_code');

lists方法查询符合条件的每条记录的name字段(或者查询符合条件的每条记录的name字段以id_code为键名)

$select_field = DB::table('carousel')->select('id','id_code','name')->get();

select方法查询符合条件的每条记录的指定字段

DB::table('user_log')->chunk(10, function($number){

var_dump($number);

if(???) return false;

});

chunk方法根据条件每次查询固定的记录,同时内部的回调函数可以控制流程,当满足某个条件的时候可以return false

dd($select_field);

(5)聚合函数:

$count = DB::table('city')->count();

统计总的记录数

$max = DB::table('user_log')->max('id');

求最大值

$min = DB::table('user_log')->min('id');

求最小值

$avg = DB::table('user_log')->avg('id');

求平均值

$sum = DB::table('user_log')->sum('user_id');

求和

dd($sum);

注意:

1.dd(“输出打印”) 会阻塞程序的向下运行,建议使用 print_r(“xxxx”) 进行打印测试

2.数据库的返回的是数组集,它不能被当成数组切割,所以需要使用toArray()方法转换成数组。

1.数据库中定义的类型

​ Int类型的字段,可以通过string 和number类型的访问

2.表单传递的数据类型

number类型的数据全部转换成String类型

7.框架默认模块
1.validator验证数据
use Illuminate\Support\Facades\Validator;     // 后台表单验证

public function doLogin(Request $request){

        // 1、接收表单提交的数据
        $input = $request->except('_token') ;
        //2.进行表单验证

        $rule = [
            'username'=>'required|between:4,18',
            'password'=>'required|between:4,18|alpha_dash',
            'code'=>'required|between:4,4',
            'hidden_code'=>'required|between:4,4',
        ];

        $msg = [
            'username.required'=>'用户名必须输入',
            'username.between'=>'用户名长度必须在4-18位之间',
            'password.required'=>'密码必须输入',
            'password.between'=>'密码长度必须在4-18位之间',
            'password.alpha_dash'=>'密码必须是数组字母下滑线',
            'code.between'=>'验证码必需是4位数字',
        ];

        
        $validator = Validator::make($input,$rule,$msg) ;   // 表单数据, 验证规则, 错误信息

        if ($validator->fails()) {
            return redirect('admin/login')
                ->withErrors($validator)
                ->withInput();
        }

    }
    
  显示结果
   @if(count($errors) > 0)
            <div class="alert alert-danger">
                <ul>
                @foreach ($errors->all() as $error)
                     <li>{{ $error }}</li>
                @endforeach
             </ul>
            </div>
        @endif

2.Crypt密码加密(-)
use Illuminate\Support\Facades\Hash;           // 哈希加密
use Illuminate\Support\Facades\Crypt;           // crypt加密

 // 加密算法
    public function jiami(){
        // 1.md5加密, 生成一个32位的字符串(内置函数)
//        $str = '123456';
//        return md5($str);

        //  2.哈希加密
//        $str = '123456';
//        $hash = Hash::make($str);
//
//        if(Hash::check($str,$hash)){
//            return '密码正确';
//        }else{
//            return '密码错误';
//        }

        // 3.crypt加密
        $str = '123456';
        $crypt_str = Crypt::encrypt($str);
//        return $crypt_str;
        if($str == Crypt::decrypt($crypt_str)){
            return '密码正确';
        }else{
            return '密码错误';
        }
 
    }

3.paginate分页模块
  public function index()
    {
        $user = User::paginate(3);          // 进行分页
        return view('admin.user.list',compact("user"));      // 传入user对象
    }
8.Middleware中间件(api)

app/Http/MIddleware 中间件是与控制器的功能 相关联

不要使用AuthorityMiddleware作为 中间件的名称 ,需要改成其它名称 (如:AuthTokenMiddleware), 因为前面的名称下的文件作为中间件,会出现 无权限访问的情况。

1.全局中间件
如果你想要定义的中间件在每一个 HTTP 请求时都被执行,只需要将相应的中间件类添加到 app/Http/Kernel.php 的数组属性 $middleware 中即可。

protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

2.分配中间件到指定路由
如果你想要分配中间件到指定路由,首先应该在 app/Http/Kernel.php 文件中分配给该中间件一个 key,默认情况下,该类的 $routeMiddleware 属性包含了 Laravel 自带的中间件,要添加你自己的中间件,只需要将其追加到后面并为其分配一个 key

protected $routeMiddleware = [
         .......
        'cors' => Middleware\CorsMiddleware::class,
         .....
 ]
   Route::group(['prefix'=>'admin','namespace'=>'Admin','middleware'=>'AuthLogin'],function(){
    .....
})

3.中间件组
有时候你可能想要通过指定一个键名的方式将相关中间件分到同一个组里面,这样可以更方便地将其分配到路由中,这可以通过使用 HTTP Kernel 提供的 $middlewareGroups 属性实现。

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];
1.laravel-cors跨域

1.安装模块

composer require fruitcake/laravel-cors --ignore-platform-reqs 

2.安装CorsMiddleware中间件

php artisan make:middleware CorsRequestMiddleware

3.handle替换以下的内容

  public function handle(Request $request, Closure $next)
    {
        header('Content-Type: text/html;charset=utf-8');
        header('Access-Control-Allow-Origin:*');
        header('Access-Control-Allow-Methods:POST,GET,PUT,OPTIONS,DELETE'); // 允许请求的类型
        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
        header('Access-Control-Allow-Headers: Content-Type,Access-Control-Allow-Origin,Access-token,Content-Length,Accept-Encoding,X-Requested-with, Origin,Access-Control-Allow-Methods'); // 设置允许自定义请求头的字段

        return $next($request);
    }

4.在Kernel.php核心中添加全局中间件

    protected $middleware = [
       .... 
//      全局中间件:身份认证中间件, 跨域中间件
        Middleware\AuthTokenMiddleware::class, 
        Middleware\CorsRequestMiddleware::class,
    ];

**注意: **跨域优先原则

// 全局中间件:身份认证中间件, 跨域中间件

// 注意: 跨域优先原则 :需要先跨域,再进行身份认证。
2.jwt-auth用户认证

1.使用composer安装新模块

安装 tymondesigns/jwt-auth
composer require tymon/jwt-auth --ignore-platform-reqs

2.添加配置(修改config/app.php)

//providers元素中添加:
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,

//aliases元素中添加:
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,

3.发布配置(生成config/jwt.php)

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
//会自动生成一个配置文件:config/jwt.php

可以设置有效时间

//  ttl单位: 分钟  ;   总共 15天
'ttl' => env('JWT_TTL', 15*24*60),

4.生成 key (自动生成 .env)

php artisan jwt:secret
//.env 文件下生成一个加密密钥 

5.生成token模型(修改Model/Users.php)

在Model/Users.php(自定义 )中实现jwt接口,并新增加两个方法

实现JWTSubject接口,实现getJWTIdentifier()和getJWTCustomClaims()方法

use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    ...
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }
    
    public function getJWTCustomClaims()
    {
        return [];
    }
}

6.修改认证的方法(修改 auth.php)

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    
    // 新添加以下内容
    // 原来是 token 改成jwt
	'api' => [
		'driver' => 'jwt',   
		'provider' => 'users',
	],
],

7.新建AuthController (官方模板)

复制以下内容,放在自定义的控制器中

login登录,me获取用户信息,logout退出登录,refresh刷新token,respondWithToken返回token

<?php
namespace App\Http\Controllers;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     * 要求附带email和password(数据来源users表)
     * @return void
     */
    public function __construct()
    {
        // 这里额外注意了:官方文档样例中只除外了『login』
        // 这样的结果是,token 只能在有效期以内进行刷新,过期无法刷新
        // 如果把 refresh 也放进去,token 即使过期但仍在刷新期以内也可刷新
        // 不过刷新一次作废
        $this->middleware('auth:api', ['except' => ['login']]);
        // 另外关于上面的中间件,官方文档写的是『auth:api』
        // 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回
    }

    /**
     * Get a JWT via given credentials.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login()
    {
        $user = User::where("username",$input['username'])->first();;

        if (! $token = auth('api')->login($user)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth('api')->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth('api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     * 刷新token,如果开启黑名单,以前的token便会失效。
     * 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth('api')->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60
        ]);
    }
}

8.添加路由(routes/web.php, 可以省略)

Route::group(['prefix' => 'auth'], function(){
    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('me', 'AuthController@me');
});
9.schedule定时任务
1.laravel定时任务

(1.启动命令

php artisan make:command TestCommand   创建TestCommand定时任务(实际效果只执行了一次)

php artisan schedule:run               启动定时任务

(2.配置项目

在新生成的App\Console\Commands\TestCommand配置以下内容


use App\Models\User;             // 引入模型

class TestCommand extends Command
{ 
    protected $signature = 'command:test'; // 名称
    
    // 执行的工作
    public function handle()
    { 
        $res = User::create(["phone"=>'123456789',"password"=>'123456']);
        return 0;
    }
}

在App\Console\Kernel.php配置以下内容


     // 注册定时任务
    protected $commands = [
        \App\Console\Commands\TestCommand::class
    ];

    // 定时任务配置
    protected function schedule(Schedule $schedule)
    {
        // $schedule->command('inspire')->hourly();
         $schedule->command('command:test')->everyMinute();
    }


(3.执行间隔时间(略)

->cron('* * * * *');    在自定义Cron调度上运行任务
->everyMinute();    每分钟运行一次任务
->everyFiveMinutes();   每五分钟运行一次任务
->everyTenMinutes();    每十分钟运行一次任务
->everyThirtyMinutes(); 每三十分钟运行一次任务
->hourly(); 每小时运行一次任务
->daily();  每天凌晨零点运行任务
->dailyAt('13:00'); 每天13:00运行任务
->twiceDaily(1, 13);    每天1:00 & 13:00运行任务
->weekly(); 每周运行一次任务
->monthly();    每月运行一次任务
->monthlyOn(4, '15:00');    每月4号15:00运行一次任务
->quarterly();  每个季度运行一次
->yearly(); 每年运行一次
->timezone('America/New_York'); 设置时区

2.Windows定时任务*

这个定时任务是laravel实现定时任务成功的关键

Laravel框架设置定时任务windows_我王十一呀的博客-CSDN博客_laravel windows 定时任务

(1.创建脚本

首先在项目下建立一个laravel-schedule.bat文件

注意点:不要有任务注释, 必须使用 换行 分隔脚本命令


cd C:\wamp64\www\php_demo\giteeWarehouse\report-project\WebServe--report

php artisan schedule:run

可以在终端进行输入文件名进行脚本测试:

  1. 在当前文件夹下执行: laravel-schedule.bat
  2. 在全局路径下执行:C:\wamp64\www\php_demo\giteeWarehouse\report-project\WebServe–report\laravel-schedule.bat

如果可以正常执行,则证明运行正常。

复制bat文件所在的绝对路径

C:\wamp64\www\php_demo\giteeWarehouse\report-project\WebServe--report\laravel-schedule.bat

(2.创建Win服务

  1. 打开任务进程 taskschd.msc --》 点击右侧的“创建基本任务向导” --》 注意:选择 “计算机启动时”

在选择"脚本"时,把上方的脚本路径复制上, 设置名称为 laravel-schedule

  1. 然后进行配置任务属性 (任务详情)–》 设置 “触发器” --》 新建触发器,设置间隔时间(最小是5分钟),可以选择持续时间“无限期”, 然后在上一层的“设置”选项卡切换 “并行运行新实例”–》
  2. 屏蔽cmd窗口: 选择 “常规”选择卡下的 “更改用户或组”–》 选择 “高级”, “立即查找” --》 选择 “SYSTEM”, 最后点击确认

3.Laravel 全栈学习

1.流程模板语法

模板页面中,可以使用php语法进行运算。但如果需要插入到页面中,则需要使用插值语法。

$errors->first()                 //获取数组的第一个元素

$errors->all()                   // 获取数据的所有元素(相当于拷贝一个对象), 可省略

count($error)                   // 即使$error是一个空值,或者是一个字符串也这样使用
1.@foreach循环语句
{{--模板语法--}}
@foreach($user as $v)
    <tr>
        <td>{{$v->id}}</td>
        <td>{{$v->username}}</td>
        <td>{{$v->password}}</td>
        <td><a href="/user/edit/{{$v->id}}">修改</a>
            <a href="">删除</a>
        </td>
    </tr>
@endforeach


// 枚举数据
3.枚举数据,有序号(序号标识)
 @foreach ($b as $key => $item) { 
     echo $key.','.$item." ; 
}
---- 使用含layui的模板语法

        {{--错误信息提示栏--}}
        @if(count($errors) > 0)
            @if(is_object($errors))
                <script>
                    layer.msg("{{$errors->first()}}")
                </script>
            @else
                <script>
                    layer.msg("{{$errors}}")
                </script>
            @endif
        @endif
**2.@if 条件判断 **
@if(xxxx)
@elseif(xxx)
@else
@endif
@if(count($errors) > 0)
        <div class="alert alert-danger">
            <ul>
                @if(is_object($errors))
                    @foreach ($errors->all() as $error)
                         <li>{{ $error }}</li>
                    @endforeach
                @else
                    <li>{{ $errors }}</li>
                @endif
            </ul>
        </div>
    @endif
3.{{ }} 插值语法

Django是{% %} 而laravel是{{ }} 和 @, 它不受标签和节点的限制, 它本身相当于一个变量,

在标签内使用的时候与vue不同,它需要使用{{ }} ,但它也需要引号

<form action="{{url("user/update")}}" method="post">
    <table>
        <tr>
            {{ csrf_field()  }}
            {{--<input type="hidden" name="_token" value="{{ csrf_token() }}">--}}
            <input type="hidden" name="id" value="{{ $user->id }}">
            <td>用户名</td>
            <td><input type="text" name="username" value="{{ $user->username }}"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="提交"></td>
        </tr>
    </table>
</form>

注意事项:

1.如果$arr是null, 而不是array()空数组, 则不能直接使用 @foreach 进行遍历。

@if($show_arr)
       @foreach( $show_arr['urls_arr'] as $item)
           <div class="preview-img-box" style="float:left; margin:10px;">
              <img src="{{$item}}"  alt="">
           </div>
       @endforeach
@endif

2.多级数据,需要先检测存在性,再取值


{{$item.info ? $item.info.scene_title : ''}}

4.{{ url( ) }}路由加载
而Laravel需要使用 路由

标签内使用 {{ url('admin/welcome') }}
5.request()->xx请求

<a href="#">{{request()->path()}}</a>   获取页面

<a href="#">{{request()->session()->get("user")["name"]}}</a>

@if( request()->session()->get("type") == 'user' )
6.URL::asset()资源加载

Django是 需要引入(根路由/) 标签内使用 {% url 路由的别名xxxx %} ,重定向redirect(“路由的别名”)

而Laravel需要使用 路由,必须由 {{URL::asset(‘css/1.css’)}} 引入静态资源(或者使用根路由/),标签内使

用 {{ url(‘admin/welcome’) }}, 重定向 redirect(" admin/hello"),back();

但是只有在返回模板视图中可以使用 view(“admi.index”) 和引入其它模板文件才会使用。

@include(“admin.user.index”),

1. URL::asset()模板语法
<link rel="stylesheet" href="{{URL::asset('css/1.css')}}"> 
    
2.设置根路由,在根目录下查找(public)
<link rel="stylesheet" href="/css/1.css">    
    

// 请求根路径上的路由, 只一般 异步和静态资源提取才会使用 “/"
// 前后端的域名是同一个域,并且访问的路由和接口路由混合在一起。
$.get('/user/del/'+id,function(e){
   console.log(e,"已经发送请求")
})
     
     
7.x.blade.php模板视图
视图文件位置 : resources/views

示例:
php网页文件:resources/views/admin/index.blade.php

被控制器调用:views("admin.index")
8.@include 模板引入
1.单纯引入模块
@include("admin.public.styles")     
// 相当于引入模板视图文件,不过可以不用注册路由, 不用添加引后结尾
// 一般模板视图文件是用.来连接,控制器和其它模块的引入用“\”反斜杠
 把相同部分抽离,当成单独的一小块
 
 2.带参数引入模块         
 @include('shared._user_info', ['user' => $user])  
9.@extends继承模板

@extends("layouts.admin")
子类继承父类页面所有元素,一般是已经将碎片化的内容组成了一个整体。
(admin.blade.php就包含footer,header,aside等相同部分的内容)

继承实战


子类继承父类模块

1.继承parent模板
@extends('parent')


2.修改插槽内容
(1 修改文本内容  
   @section('title','这是标题') 
(2 修改块级节点
    @section('right')
       <div>右侧内容</div>
    @endsection
(3 不添加节点
   空

父类模板

<body>
    <!--  (1.内容插槽 -->
	<h1>@yield("title")</h1>
	
	<div class="left">
	    <!-- (2.使用含默认值的插槽 -->
		@section('left')
		    abcd
		@show 
	</div>
	
	<div class="right"> 
       <!--  (3.块级插槽 -->
		@yield("right")
	</div> 
</body>

注意: 有些前端模板引擎中,也有可能 用{{ }}, 为防止blade模板云解析,前面加@, 阻止符号解析。

例:@{{$hello}}

2.控制器语法
1.view返回视图

@include, view 与视图文件相关的参数, 都是根据使用“.” 点来连接文件

// 后台首页
public function index(){
   return view("admin.index");
}


1.view第二个参数 (建议使用)       
	view('test.index', ['test1' => $test1,'test2' => $test2]);     // 主要以数组格式数据传递
	view('test.index', array('test1' => $test1,'test2' => $test2));

2.with方法向页面中传值
	view("admin.index")->with('test1',$test1)                   // 键值对字符串,和数组格式 
	view("admin.index")->with(['test1'=>$test1])
	view("admin.index")->with(array('test1'=>$test1,'test1'=>$test2))


3.view第二个参数使用compact方法
    view('test.index',compact('test'));                        //  可以解析字符串代表的变量
	view('test.index',compact('test1','test2' ,'test3' ));
	// $test123 = ['测试1','测试2','测试3']
	view('test.index',compact('test123'));
2.redirect跳转页面

url , redirect 与路由相关的参数, 使用 “/” 正斜杠连接文件


1.withErrors向重定向页面传值
    redirect('admin/login')->withErrors('验证码错误')

2.页面中可以使用一下变量$errors
    is_object($errors)   可以判断是否是对象
    $errors->all()       可以展开为数组
    count($errors)       获取数组的长度。
3.namespace工作空间

不管是命名工作空间,还是引入工作空间,工作空间采用 “\” 反斜框连接各个路径

namespace App\Http\Controllers\Admin;  //  引入控制器

use Illuminate\Http\Request;     //  引入控制器
use App\Models\User;             // 引入模型
use App\Http\Controllers\Controller;
4.require 模块引入
---- Admin/index.php
<?php
    use Illuminate\Support\Facades\Route;   // 使用项目路径下的路由方法


    function AdminRoutes(){  // 准备暴露的函数



        //  全线接口与视图页面混合
        //
        // 身份认证分组,  ,'middleware'=>'authority'
        Route::group(['prefix'=>'admin','namespace'=>'Admin'],function() {
            ....
            Route::get('/login', 'UserToController@add'); 
            ....
        }); 
    }

----- web.php文件

    <?php

    use Illuminate\Support\Facades\Route;

    require('Home/index.php');    // 引入自定义的模块,前台api
    require('Admin/index.php');    // 引入自定义的模块,后台api


    // 测试使用
    Route::get('/',function(){
        return 'laravel,登录成功';
    });

    //HomeIndex\HomeRoutes();    // 如果有命名空间,则使用模块内,指定命名空间的函数
    HomeRoutes();    // 如果没有命名空间,则直接使用模块内的函数
    AdminRoutes();    // 如果没有命名空间,则直接使用模块内的函数


5.autoload公共函数

(1.创建公共函数

在app/Common/functions.php文件(新创建的文件),添加以下内容

<?php

function test(){
    return 'hello';
}

(2.autoload自动加载

在composer.json中的autoload添加以下内容

然后执行命令:

composer dump-auto

或者 composer dump-auto --ignore-platform-reqs

"autoload":{
    ... 
    // 新添加的内容
    "files": [
    	"app/Common/functions.php"
    ]
}

(3.测试运行

functions.php里面的函数被暴露,可以在web.php 路由,controller.php控制器中使用

Route::get('/test', function(){
    return test();
});

// 访问路径, 显示出结果
3.中间件模块
1.csrf 跨站表单提交*

在表单提交前,暂停默认的跳转,取页代之的是异步请求

1. 在meta添加跨站声明(无效)
      <meta name="csrf-token" content=" {{ csrf_token() }}">
   
2. 或者在form表单元素的第一行添加以下内容,csrf表单跨站访问 
      {{ csrf_field()  }}
      
 注意:表单提交需要添加_token字段,否则会出现419错误
      
 //监听表单提交事件
  form.on('submit(add)', function(data){ 
       $.ajax({
        type:'POST',
        url:'/admin/user',
        dataType:'json',
        headers:{
              'X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr("content")
        },
         data:data.field,
         success:function(data){
         // 提示添加成功
            if(data.status == 0){
               layer.alert(data.message,{icon:6},function(){
                   parent.location.reload(true);  //iframe框架重新载入
               })
            }else{
                layer.alert(data.message,{icon:5})
             }
            console.log(data);
         },
           errors:function(data){
                // 错误信息
                console.log(data)
           }
         })
         return false;  // 阻止默认行为
 });
    
2.session 认证存储

注意:如果是从jwt转换成session,则需要处理jwt的配置。

查看session配置 config/session.php

// lifetime单位:分钟

'lifetime' => env('SESSION_LIFETIME', 120),

存储数据在session

// 保存用户信息到session中,并跳转主页
session()->put('userinfo',$user);

// 查看缓存的session信息
// 服务端与客户端建立一个连接,只要服务端不删除对应的session,则当前这个登录状态会一直存在。
// 服务端的session也有过期时间,
session()->get('userinfo')
 
 
// -------------------------------- 以下的内容等待测试 ------------------------------
// 从会话中获取指定的数据  
//在没有存在键的情况下,将返回的默认值指定为第二参数  
$value = $request->session()->get('key’); 
$value = $request->session()->get('key', 'default’);  
$value = $request->session()->get('key', function () { 
    return 'default'; 
}); 

// 获取会话中的所有数据 
$data = $request->session()->all();
  
// 检查指定的数据是否存在于会话中 
if ($request->session()->exists('key')) {  
    // 存在 
} 
if ($request->session()->has('key')) { 
    // null不存在 
}
 
// 将数据保存到会话 
$request->session()->put('key', 'value'); 
$request->session()->put(['key1' => 'value1', 'key2' => ‘value2']);
  
// 从会话取得指定的数据后,删除该数据 
$value = $request->session()->pull('key', 'default’); 

// 从会话中删除指定的数据 
$request->session()->forget('key');
 
// 从会话中删除所有数据 
$request->session()->flush();
 
3.AuthLogin登录中间件

AuthLoginMiddleware 用于验证用户登录状态,与session()->get(“user”) , session()->put(‘user’,xxx) 搭配使用

php artisan make:middleware AuthLoginMiddleware

  1. 创建页务逻辑

    ----app/Http/Middleware/Middleware.php 
    public function handle(Request $request, Closure $next)
        {
            // session由服务器生成,表示服务端和客户端建立了会话连接。
            if(session()->get('userinfo')){
                return $next($request);
            }else{
                return redirect('admin/login')->withErrors('没有权限进入管理面板');
            }
     }
    
  2. 声明路由中间件

    ----app/Http/Kernel.php   
    protected $routeMiddleware = [
      ....  
     'AuthLogin' => \App\Http\Middleware\AuthLoginMiddleware::class,
    // 也可以直接引入所有的中间件 
     ]
    
  3. 在定义路由时,使用中间件

    **注意:经过测试,全局中间件,会出现严重的重定向,所以只能选择使用 “路由中间件” **

    Route::group(['prefix'=>'admin','namespace'=>'Admin' ],function() { 
        // 后台登录路由
        Route::get('login', 'LoginController@login');
        Route::post('dologin', 'LoginController@doLogin');
        // 加密算法
        Route::get('jiami', 'LoginController@jiami');
    });
     
    Route::group(['prefix'=>'admin','namespace'=>'Admin','middleware'=>'AuthLogin'],function(){
        // 后台首页路由
            Route::get('index','LoginController@index');
        // 后台欢迎页
            Route::get('welcome','LoginController@welcome');
        // 后台退出登录页
            Route::get('logout','LoginController@logout');
    });
    
    
4.业务性模块
1.file文件存储(原生)

文件上传

1.由上传模块上传的文件(有temp) 
 // 执行文件上传(ajax的formData和组件上传方式通用,注意设置name为 'file')
    public function doFileUpload(Request $request){
        // 1.获取上传的临时(.temp)文件,方法一
        $temp=$request->file('file');
        // 获取上传的临时(.temp)文件,方法二
        $temp = $request->all()['file'];
        // 2.获取图片的扩展名
        $extend_name=$temp->getClientOriginalExtension();
        // 3.设置新的文件名
        $file_name = "file".time().".".$extend_name;
        $file_path = "assets/files/";
        // 4.文件存储方式一
        move_uploaded_file( $temp,$file_path.$file_name) ;
        // 文件存储方式二
        // file_put_contents( $file_path.$file_name,FILE_APPEND) ;
        return array('code'=>200,'file_url'=>'/'.$file_path.$file_name);
    }
    
 2.上传的方式
(1.layui的组件上传方式 
    {{--文件上传js--}}
    <script>
      layui.use('upload', function(){
          var upload = layui.upload;

          //执行实例
          var uploadInst = upload.render({
              elem: '#upload-file' //绑定元素
              ,url: '/admin/company/show/doFileUpload' //上传接口
              ,done: function(res){
                  //上传完毕回调
                  if(res.code === 200){
                      $('#upload_file_url').val(res["file_url"]);
                      $('.preview-img-box img').attr('src',res["file_url"]);
                      layer.msg("上传成功!")
                  }else{
                      layer.msg("上传失败!")
                  }
              }
              ,error: function(){
                  //请求异常回调
                  layer.msg("错误!上传异常!")
              }
          });
      });
    </script>

(2. Input标签非自动上传
(2.1.单文本文件上传
  <form enctype="multipart/form-data" method="post">
      <input type="text" name="userID" id="userID" value="YK91812053"> <br>
      <input type="text" name="nickname" id="nickname" value="hello"> <br>
      <input type="text" name="sex" id="sex" value="1"> <br>
      <p>上传avatar文件<input type="file" id="avatar" name="avatar"/></p>
      <p>上传bkImg文件<input type="file" id="bkImg" name="bkImg"/></p>
      <button id="import">导入</button>
  </form>

    $("#import").click(function () {
        $.ajax({
            type: 'post',
            url: "http://127.0.0.1:9999/api/user/update",
            data: 'userID=YK91812053&nickname=hello&sex=1',
            async: false,
            dataType: "json" ,
            success: function (ret) {
                alert(ret);
            }
        });
    });

(2.2 文件和普通数据混合,也就是使用文件流的形式
 $("#import").click(function (){
 	var formData = new FormData();//必须是new FormData后台才能接收到
    formData.append("userID", $("#userID").val());
    formData.append("nickname", $("#nickname").val());
    formData.append("sex", $("#sex").val());
    formData.append("avatar", $("#avatar")[0].files[0]);
    formData.append("bkImg", $("#bkImg")[0].files[0]);
    $.ajax({
        type: 'put',
        url: "http://127.0.0.1:9999/api/user/update",
        data: formData,

       //下面两个属性可以不写
        //  cache: false,  //不缓存数据
        //  dataType: "json",

        contentType: false,//必须false才会自动加上正确的Content-Type
        processData: false,//必须false才会避开jQuery对 formdata 的默认处理,XMLHttpRequest会对 formdata 进行正确的处理
        success: function (jdata) {
            alert(jdata);
        }
    });
  })









(3.divUpload 多图片 (blob+ ajax) (略)
    {{--将blob文件 + ajax多文件图片上传--}}
    <script>
        // 将base64转换为blob
        function dataURLtoFile(dataURI, type) {
            let binary = atob(dataURI.split(',')[1]);
            let array = [];
            for (let i = 0; i < binary.length; i++) {
                array.push(binary.charCodeAt(i));
            }
            return new Blob([new Uint8Array(array)], {type: type});
        }

        // 上传base64的图片
        function upload(baseStr,call_back) {
            // 图片地址
            let imgBase = baseStr;
            let blob = dataURLtoFile(imgBase, 'image/jpeg');
            var formData = new FormData();
            let fileOfBlob = new File([blob], new Date() + '.jpg'); 
            // 重命名了
            formData.append("file", fileOfBlob);
            $.ajax({
                url:  "/admin/company/show/doFileUpload", //用于文件上传的服务器端请求地址
                dataType: 'json',
                type: 'POST',
                async: false,
                data: formData,
                processData: false, // 使数据不做处理
                contentType: false, // 不要设置Content-Type请求头
                success: function (res) { //服务器成功响应处理函数

                    //上传完毕回调
                    if(res.code === 200){
                        // $('#upload_file_url').val(res["file_url"]);
                        // $('.preview-img-box img').attr('src',res["file_url"]);
                        layer.msg("上传成功!")
                        // 执行回调函数
                        call_back(res["file_url"]);
                    }else{
                        layer.msg("上传失败!")
                    }  
                }
            });
            
            
        // 点击上传功能按键
        $(function(){
            var $tgaUpload = $('#goodsUpload').diyUpload({ 
                success:function( data ) { },
                error:function( err ) { },
                buttonText : '',
                accept: {
                    title: "Images",
                    extensions: 'gif,jpg,jpeg,bmp,png'
                },
                thumb:{
                    width:120,
                    height:90,
                    quality:100,
                    allowMagnify:true,
                    crop:true,
                    type:"image/jpeg"
                }
            });


        });
        }

    </script>
     
     // 将blob的图片上传,并连接这些图片地址
   let imgUrls = $('.upload-ul>.diyUploadHover > .viewThumb img');
   let len = imgUrls.length;
   if(len > 0){
         let arr = []
          for(let i=0;i<len;i++){
            // 这里会出现问题,失真,以及无法生成闭包
             upload(imgUrls[i].src,function callback(e){
                 arr.push(e)
             });
           }
        new_data['company_market_urls']  = arr.join('|');
     }

组件的上传

组件的上传是会将上传的文件二进行制数据,放在data,(默认Post方法)。

键值是  file: (二进行数据)

客户端文档下载

客户端访问服务端,服务端返回一个公共路径下的文件url,
利用浏览器的不支持非网页文件,就会下载的特性。

window.open(xxxx.xlsx)

<a href="xxxx.pdf" />

客户端图片下载

a标签下载图片。需要设置相关的参数。(还有兼容性控制),否则设置了download属性还是会打开。

a标签不能下载没有后缀名的文件(下载的结果无效)
<a href="https://cdn.jsdelivr.net/gh/JackKetty/PicGoCDN/TyporaArticle/202204102316049.png" download = "https://cdn.jsdelivr.net/gh/JackKetty/PicGoCDN/TyporaArticle/202204102316049.png" >链接内容</a>
2.Storage 文件存储(??)

(1.配置文件

在config/filesystem中配置以下内容

'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],
        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],
		'upload' => [
            'driver' => 'local',
            'root' => storage_path('app/public/upload'),
            'url' => env('APP_URL').'/upload',
            'visibility' => 'public',
        ],
        
    ], 

**(2.控制器中使用 **

//获取原文件名
$originalName = $fileinfo->getClientOriginalName();
//获取扩展名
$ext = $fileinfo->getClientOriginalExtension();
//重命名文件
$name ='filename'.'.'.$ext;
//获取文件临时路径
$realPath = $fileinfo->getRealPath();
//获取文件内容
$file = file_get_contents($realPath )

//获取指定路径
Storage::disk('public') 
//存储文件
Storage::disk('配置的disk名')->put('subpath','文件内容');
3.mail邮件发送

Laravel包含邮件发送模块,并且默认使用STMP形式发送邮件

全局配置文件 config/mail.php, 但 不建议去改

(1.配置.env文件



MAIL_MAILER=smtp
MAIL_HOST=smtp.qq.com      # 修改成指定的邮箱客户端
MAIL_PORT=25               # 端口 
MAIL_USERNAME=3152860240@qq.com       # 发件人邮箱
MAIL_PASSWORD=otgrumgmsjkydhbg        # 发件人密钥
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=3152860240@qq.com   # 发件人的引用地址
MAIL_FROM_NAME="${APP_NAME}"          # 发件人的名称


(2.创建 .blade.php模板文件

在resources/views下建立 emails/test.blade.php 文件

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    {{$name}} 你好,这是一封测试文件
</body>
</html>

(3.控制器发送邮件


    use Illuminate\Support\Facades\Mail; # 一般来说,框架默认模块都是Illuminate开头


    // 发送邮件 
    public function sendEmail(){
        try{
            // (1.发送邮件
            // Mail::send()的返回值为空,所以可以其他方法进行判断
            // 第一个参数是模板,第二个参数是传递的参数,第三个是接收人
            Mail::send('emails.test',['name'=>'小明'],function($message){
                $message ->to('2276974147@qq.com')->subject('邮件测试');
            });
            // (2.查看结果
            // 返回的一个错误数组,利用此可以判断是否发送成功
            $res = Mail::failures();
            if(!$res || ( is_array($res) && !count($res))){
                return '发送邮件成功 !';
            }else{
                return '发送邮件失败 !';
            }
        }catch (Exception $e){
            return '发送邮件出现异常'.$e;
        }
    }




(4.其它类型的文件

1.纯文本文件
Mail::raw('你好,我是PHP程序!', function ($message) { 
    $message ->to('282584778@qq.com')->subject('纯文本信息邮件测试');
});

2.含远程图片的文件(无效)
只需要修改一下.blade.php模板
<img src='{{$message->embed($image)}}'> 


3.含本地图片的文件
和远程网络图片的方法不同:<img src="{{$message->embedData($image,'我的自拍照.jpg')}}">

如果是使用static里面的静态文件,则可以使用根 路径 “/”的方式获取。
如果是使用 Storage/images文件夹里面的文件,则需要通过Storage获取路径 。
$images = Storage::get('images/obama.jpg')


4.邮件附件
Mail::send('emails.test',['name'=>$name],function($message{
    $message->to('282584778@qq.com';)->subject('邮件主题');
    // 本地保存的文件
    $attachment = storage_path('app/files/test.txt');
    $message->attach($attachment,['as'=>'中文文档.txt']);
    // 更安全的写法 
    $message->attach($attachment,['as'=>"=?UTF-8?B?".base64_encode('中文文档')."?=.txt"]);
});

(5.封装的函数


// 发送邮件
// template , name ,data,  content , user ,   title,  (attach)
function sendEmail($options){
    try{
        // Mail::send()的返回值为空,所以可以其他方法进行判断
        // 第一个参数是模板,第二个参数是传递的参数,第三个是接收人
        Mail::send('emails.'.$options['template'],['name'=>$options['name'],'data'=>$options['data'],'content'=>$options['content']],
            function($message)use($options){

            $message ->to($options['user'])->subject($options['title']);
            if(isset($options['attach'])){
                $message->attach($options['attach']['path'],['as'=>$options['attach']['name']]);
            }
        });
        // 返回的一个错误数组,利用此可以判断是否发送成功
        $res = Mail::failures();
        if(!$res || ( is_array($res) && !count($res))){
            return '发送邮件成功';
        }else{
            return '发送邮件失败';
        }
    }catch (Exception $e){
        return '发送邮件出现异常'.$e;
    }
}


// 调用接口 
public function test1(){
    // (1.获取天气信息
    $data = parseWeather();
    // (2.管理员设置的信息
    $content = [
        'keywords'=>'暴雨预警',
        'duration'=>'2022-08-04 04:00:00-2022-08-06 04:00:00',
        'body'=>'在暴雨来临前,注意天气的变化!收到请在工作群里面回复!',
    ];
    // (3.收件人信息
    $user = [
        'name'=>'小明',
        'user'=>'2276974147@qq.com'
    ];
    // 发送邮件
    $res = sendEmail([
        'template'=>'info',
        'name'=>$user['name'],
        'user'=>$user['user'],
        'data'=>$data,
        'content'=>$content,
        'title'=>$content['keywords'],
    ]);
    return $res;
}


// 模板页面
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        p{
            text-indent: 2rem!important;
        }
        p img{
            height:100px;
            width: 100px;
        }
        p.link{
            text-indent: 0rem!important;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
        }
        a{
            text-decoration: none;
        }
        a:hover{
            color:red;
        }
    </style>
</head>
<body> 
    <p>
        【{{$content['keywords']}}】:您好!员工{{$name}}。
    </p>
    <p>
        当前郑州天气:{{$data['weather']}},温度 {{$data['temperature']}},湿度 {{$data['humidity']}}。
        请在{{$content['duration']}}时间段内 ,{{$content['body']}}
    </p>
    <p class="link">
        <img src="{{$data['banner']}}" alt="">
        <a href="{{$data['url']}}" alt="链接">查看当前郑州天气</a>
    </p>
</body>
</html>

5.layui插件库(建议)

前言:layui更多的是使用js调用方法动态生成对应的组件,与bootstrap前端UI库不一样,

1.使用前言
1.引入模块

注意:官网去下载最新版本的layui,从本地引入js,css。

Layui - 经典开源模块化前端 UI 框架

远程cdn 没有更新到最新的内容

  1. 因为larave属于后端框架,并不能使用composer安装前端模块,所以,前端的需要使用的模块,则需要下载到本地,使用script脚本的方式引入。

  2. 不建议使用在线cdn的方式引入模块,会对性能与稳定性有一定影响。

2.layui特性
1.layui属于基于js生成页面节点,是提前渲染模板。
  常用 标签layer-filter =  'xxx' 配合 layui.user(xxxx,function(){ ..... })  

2.配合xadmin后台框架
   
   1.xadmin有特殊的侧边栏 iframe控制项
   
   2.xadmin有特殊的弹层  onclick="x_admin_show('编辑','/admin/college/intern/inner')"
2.使用方法
---- 本地脚本引入

<link rel="stylesheet" href="/static/lib/layui-v2.6.8/css/layui.css">

<script type="text/javascript" src="/static/lib/layui-v2.6.8/layui.js"></script>


---- html文件内容
<button class="layui-btn" id="demo1">
    下拉菜单
    <i class="layui-icon layui-icon-down layui-font-12"></i>
</button> 


----- js文件内容
	
    {{--对话框--}}
    layer.confirm('is not?', {icon: 3, title:'提示'}, function(index){
            //do something
          layer.close(index);
    });  
    layer.msg('hello');

    {{--下拉列表--}}
    <script> 
        layui.use('dropdown', function(){
            var dropdown = layui.dropdown
            dropdown.render({
                elem: '#demo1' //可绑定在任意元素中,此处以上述按钮为例
                ,data: [{
                    title: 'menu item 1'
                    ,id: 100
                    ,href: '#'
                },{
                    title: 'menu item 2'
                    ,id: 101
                    ,href: 'https://www.layuion.com/' //开启超链接
                    ,target: '_blank' //新窗口方式打开
                } ]
                ,id: 'demo1'
                //菜单被点击的事件
                ,click: function(obj){
                    console.log(obj);
                    layer.msg('回调返回的参数已显示再控制台');
                }
            });
        });
    </script>

1.msg消息提示栏
        {{--错误信息提示栏--}}
        @if(count($errors) > 0)
            @if(is_object($errors))
                <script>
                    layer.msg("{{$errors->first()}}")
                </script>
            @else
                <script>
                    layer.msg("{{$errors}}")
                </script>
            @endif
        @endif
2.confirm确认对话框
//eg1
layer.confirm('is not?', {icon: 3, title:'提示'}, function(index){
  //do something
  
  layer.close(index);
},function(){
   ...
});
3.select下拉列表

由于layui经过特殊处理,所以使用onchange和onclick都无法监听到下拉框的事件

注意: 父类元素添加class="layui-form"

<div class="layui-form-item">
    <label for="type"  class="layui-form-label">身份类型:</label>
    <div class="layui-input-inline">
        <select name="type" lay-verify="required" lay-filter="select1" id="select1">
            <option value="" selected>请选择身份类型</option>
            <option value="student" >学生</option>
            <option value="college">高校</option>
            <option value="company">企业</option>
        </select>
    </div>
</div>


{{--身份类型切换--}}
<script type="text/javascript">
    layui.use(['layer', 'jquery', 'form'], function () {
        var layer = layui.layer;
        var $ = layui.jquery;
        var form = layui.form;

        form.on('select(select1)', function (data) {
            alert($("#select1").val());
        });
    });
</script>


4.laypage分页栏(前端分页)
{{--内容栏--}}
<div class="layui-panel">
    <ul class="layui-menu college-ul" id="docDemoMenu1" style="display: none">
        @foreach($announce_arr as $item)
            <li style="display: none">
                 <img src="{{$item["cover_url"]}}"/>
                <div class="announce-box">
                    <div class="item-top text-truncate-1">{{$item["announce_title"]}}</div>
                    <div class="item-middle text-truncate-1">{{$item["announce_target"]}}:{{$item["announce_content"]}}</div>
                    <div class="item-bottom">
                        <span>开始时间:{{$item["announce_start_time"]}}</span>
                        <span>截止时间:{{$item["announce_end_time"]}}</span>
                        <span>发布时间: {{$item["announce_publish_time"]}}</span>
                    </div>
                </div>
            </li>
            <li class="layui-menu-item-divider"></li>
        @endforeach
    </ul>
</div>
{{--分页栏--}}
<div class="page">
    <div id="page-div"></div>
</div>



<script>
 layui.use(['laypage', 'layer'], function(){
     var laypage = layui.laypage
         ,layer = layui.layer;

     // 获取节点数据
     var data = document.querySelectorAll('#docDemoMenu1 li');

     //调用分页
     laypage.render({
         elem: 'page-div'
         ,limit:5
         ,count: data.length /2
         ,jump: function(obj){
             // 节点数据只可以遍历, 不可以切割
             var arr = []  ,thisData = [];
             // thisData = data.splice(obj.curr*obj.limit - obj.limit, obj.limit)
             data.forEach((item,index)=>{
                 if(index >= obj.curr*obj.limit * 2 - obj.limit * 2 && index < obj.curr*obj.limit * 2 ){
                     item.style.display='flex';
                 }else{
                     item.style.display='none';
                 }
             })

             document.getElementById('docDemoMenu1').style.display = 'block';
         }
     });

 });
</script>


5.form更新渲染
 function refreshForm (){
    layui.use(['form', 'layer'], function(){
          var form = layui.form ;

         form.render(); //更新全部
   })
 }
6.editor编辑器插件

1.获取html字符串

使用easymde 编辑器,向数据库中存入html字符串

2.渲染html字符串


<div class="layui-input-inline" id="project-experience">
	<div style="display: none" id="project-experience-inner">		  {{$resume_arr['project_experience']}}</div>
	<script>
		let str = $("#project-experience-inner").text();
		$('#project-experience').html(str);
	</script>
</div>
7.progress进度条
 layui.use('element', function(){
          var element = layui.element;

          //一些事件触发
          $('#spider').click(function(){
                $(".content-div").css("display","block")
                $(".content-div .tip-div").html("爬取数据中")
                let turns = 0;
                let timer = setInterval(()=>{
                    element.progress('demo', turns+'%')
                    if(turns<100)
                        turns += 10;
                },500)
          })
        });
        


      <div class="content-div">
          <div class="tip-div">获取数据中</div>
          <div class="layui-progress layui-progress-big" lay-filter="demo" lay-showPercent="true">
              <div class="layui-progress-bar" lay-percent="0%"></div>
          </div>
      </div>
        
8.open弹出层的使用

注意点:如果是页面层里面有select下拉菜单的话,就会有问题

因此,可以考虑隐藏, 开关的办法。

//页面层

layer.open({
  type: 1,
  skin: 'layui-layer-rim', //加上边框
  area: ['420px', '240px'], //宽高
  content: 'html内容'
});

//iframe 层 
layer.open({
  type: 2,
  title: 'layer mobile页',
  shadeClose: true,
  shade: 0.8,
  area: ['380px', '90%'],
  content: 'mobile/' //iframe的url
}); 
/*隐藏方法*/
#layui-layer-shade1,#layui-layer1{
	display:none;
}


<script>
    /*加载弹层*/
    layer.open({
      type: 1,
      area: ['620px', '240px'], //宽高
      cancel: function(index, layero){    // 监听关闭事件,隐藏弹层
        $("#layui-layer-shade1").css("display","none")
        $("#layui-layer1").css("display","none")
        return false;
      },
      content: `
        <form class="layui-form" action="">
          <div class="layui-form-item">
            <label for="type"  class="layui-form-label">类型:</label>
            <div class="layui-input-block">
                <select name="score_type" lay-verify="required" lay-filter="select1" id="select1">
                    <option value="english" selected >外语成绩</option>
                    <option value="article">论文成绩</option>
                </select>
            </div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">成绩</label>
            <div class="layui-input-block">
              <input type="text" name="submit_name" required  lay-verify="required" max-length="15" placeholder="请输入课题名称" autocomplete="off" class="layui-input">
            </div>
          </div>
          <div class="layui-form-item">
            <div class="layui-input-block">
              <button class="layui-btn"  onclick="submitForm()"  >立即提交</button>
              <button type="reset" class="layui-btn layui-btn-primary">重置</button>
            </div>
          </div>
        </form>
      `
    });
       
    // (打开弹出层)
    function setSelectTitle(){ //  #layui-layer-shade1,#layui-layer1
       $("#layui-layer-shade1").css("display","block")
       $("#layui-layer1").css("display","block")
    }
    // (关闭弹出层)
    function closeSelectTitle(){
       $("#layui-layer-shade1").css("display","none")
       $("#layui-layer1").css("display","none")
   }
 
</script>



问题一:无法自由显示弹层
解决方法:自动加载弹层,使用css显示与 隐藏

问题二:下拉框无法正常 显示
解决方法:使用重置按钮进行重新渲染
 // 可以设置“重置”,进行显示下拉框
 $("button[type^=reset]").click();

问题三:无法监听到 表单的提交
因为数据监听,已经被表单拦截了,所以只有在标签内定义事件触发函数,替换原来的表单提交按钮 。
<button class="layui-btn"  onclick="submitForm()"  >立即提交</button>
9.多选下拉框

先 下载 formSelects-v4 扩展模块

//全局定义一次, 加载formSelects
layui.config({
    base: '/static/lib/layui-formSelects-master/dist/' //此处路径请自行处理, 可以使用绝对路径
}).extend({
    formSelects: 'formSelects-v4'
});
//加载模块
layui.use(['jquery', 'formSelects'], function () {
    //初始化select多选
    var formSelects = layui.formSelects;
    formSelects.btns('send_department', []);
});



<select name="send_department" lay-verify="required" lay-filter="select2" id="select2" xm-select="send_department" xm-select-type="1">
    <option value="=" >等于</option>
    <option value=">">大于</option>
    <option value=">">小于</option>
    <option value=">,<">区间范围</option>
</select>



// 对多选框进行赋值,数组形式 
layui.formSelects.value('send_department', send_department_t)
layui.formSelects.value('send_name',send_name_t)

// 对多选框进行取值,数组形式
let send_department =  layui.formSelects.value('send_department')
let send_name = layui.formSelects.value('send_name')
3.优雅的方案
1.data-列表项

// 修改表格
let trList = $("#table-list tr");
trList.each((index,item)=>{
  if($(item)[0].dataset.id == id){
      console.log("已经找到当前id",id);
      $(item).html( `
        <td>${res.data.name}</td>
        <td>${res.data.student_name}</td>
        <td>${res.data.count}</td>
        <td>
            <div class="tpl-table-black-operation">
                <a href="javascript:;" class="tpl-table-black-operation-del" onclick="onConfirmGroup('${res.data.id}','${res.data.name}','${res.data.count}')">
                    <i class="am-icon-life-ring"></i> 选择
                </a>
            </div>
        </td>
      ` );
  }
})

// 删除表格
let trList = $("#table-list tr");
trList.each((index,item)=>{
    if($(item)[0].dataset.id == id){
        $(item).remove();
    }
})


// 序号重新排序
// eq 是从0开始的,并不是从1开始,和css的nth-child不一样
// jquery的更新是在当前操作完成后,再执行dom的修改
let turn = 0;
trList.each((index,item)=>{
    if($(item)[0].dataset.id != id){
        turn += 1; 
        $(item).children('td:eq(0)').html(turn);
    }
})
2.form刷新页面
<form > 
    <input type="submit" value="刷新" /> 
</form> 

解释:form提交表单一定会触发当前页面的响应。

6.Amaze-ui插件库(不建议)
1.使用前言
1.引入模块
同layui模块一样,通过css,和js引入
2.使用方法
1.消息提示栏

    {{--错误信息提示栏--}}
    @if(count($errors) > 0)
        <div class="am-alert am-alert-success" data-am-alert style="position: absolute;width: 600px;top:20px; left: calc(50vw - 300px);text-align: center">
            {{$errors->first()}}
        </div>
        <script>
            $('.am-alert').alert()
            setTimeout(()=>{
                $('.am-alert').alert('close')
            },1500)
        </script>
    @endif
2.确认对话框(不建议)*

<div class="am-modal am-modal-confirm" tabindex="-1" id="my-confirm">
    <div class="am-modal-dialog">
        <div class="am-modal-hd">提示框</div>
        <div class="am-modal-bd" id="confirm-txt">
            请确认要删除这条记录吗?
        </div>
        <div class="am-modal-footer">
            <span class="am-modal-btn" data-am-modal-confirm>确定</span>
            <span class="am-modal-btn" data-am-modal-cancel>取消</span>
        </div>
    </div>
</div>

{{--
<script>
    $('#my-confirm').modal({
        relatedTarget: this,
        onConfirm: function(options) {
            console.log("确认了")
        },
        // closeOnConfirm: false,
        dimmer:false,
        onCancel: function() {
            console.log("取消了")
        }
    });
     // 清除实例 (无效)
        $('#my-confirm').on('closed.modal.amui', function() {
                $(this).removeData('amui.modal');
        });
        
      // 通过 创建节点,和删除节点来实现。(有效)
      function addDom(){
           ...
      }
      function removeDom(){
           ...
      }
</script>
--}}

3.form表格重置

// 解析数据
// 注意,对于每个input表单元素都会进入计算
let [{value:email},{value:phone},{value:qq},{value:password},{value:name},{value:introduce}] = document.forms[0];


// 重置表格数据
 <input type="reset"  id="reset" style="display: none" />
 
$('input[type^="reset"]').click()
4.file类型文件上传
-- js上传文件
<script>
    function changeFile(){
         var formData = new FormData()
        formData.append('file',$('#upload-input')[0].files[0])
        $.ajax({
            type:'post',
            // 另一个项目存储文件的接口
            url:getImgDomain()+'/home/project/saveImg',
            data:formData,
            contentType:false,
            processData:false,
            success:function(res){
                Message('文件上传成功!','success')
                $("#img-preview").attr('src', getImgDomain()+res.file_url)
            },
            error:function(err){
                Message("文件上传失败!请检查接口!",'danger')
            }
        })
    }
</script>
    

--- html结构
<div class="am-form-group">
    <label for="img_url" class="am-u-sm-2 am-form-label">封面图 <span class="tpl-form-line-small-title">Image</span></label>
    <div class="am-u-sm-10">
        <div class="am-form-group am-form-file">
            <div class="tpl-form-file-img">
                <img src="" alt="" id="img-preview">
            </div>
            <button type="button" class="am-btn am-btn-danger am-btn-sm">
                <i class="am-icon-cloud-upload"></i> 添加封面图片</button>
            <input id="upload-input" name="img_url" type="file" accept="image/*" onchange="changeFile()">
        </div> 
    </div>
</div>
    
7.项目开发详情(略)
1.分离项目区别
1.数据格式
  不分离项目,可以返回view ,和 json数据 (二维数组)。
  分离项目一般只返回json, 服务端渲染返回view

2.表单交互
  不分离项目,多使用form + ajax 进行表单提交。 form表单提交可以被ajax拦截。
  分离项目多使用ajax表单提交。
  
3.资源共享 
   不分离项目,默认是相同域进行访问。任何的文件,都在同域下进行。包括图片,前端的ajax请求路径。
   分离项目,任何文件数据都需要注明域名。以表示文件的来源。
   
4.认证跨域
  不分离项目,使用session()进行身份认证,不用跨域, 多占用服务资源。session会随着服务端部署而改变
  分离项目,使用jwt方案,在header添加token, 后端需要跨域。 jwt不会因为服务端布署而改变。(crypt密码加密除外)
  
5.数据缓存
  不分离项目,多利用服务端缓存的session , 和多次的数据库查询。非常花费服务端资源
  分离项目,多利用客户端的localStorage,sessionStorage, 一般需要减少和服务端的交互。
  
6.项目模板
  不分离项目,有模板语法。该语法是在生成html前,对页面进行插入遍历,然后才返回给客户端。,
  分离项目,有框架的模板语法,根据前端框架框架规则,动态生成页面。然后返回给客户端。
  
7.页面特效
  不分离项目,服务端不是非常容易做特效。 使用的任何组件,都是基于DOM的操作。非常消耗性能。
  分离项目,客户端有非常大的开发空间。不受过多的限制。加上框架的模板语法,让特效变得非常容易。
2.php语法差异
1.php有class,和function

2.php没有. 与对象,并且不能使用this. 访问类内的方法

3.php常用箭头代替.号。$this-> except  获取类内的方法  , xxx->first()    表示含义 就是 this.   和 xxx.first()

.env文件 中 定义连接的数据库  以**db_**开头的数据
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=shorthand
DB_USERNAME=root
DB_PASSWORD=123456
3.php后端注意

//  php不允许请求头上的字段带有下划线的数据_,因为它会默认转换为-
//  php不允许请求头上的字段带有驼峰习惯的数据,因为它会默认转换为小写
4.快速开发项目
1.先选择view页面(包括组件库),布局页面

2.建立模型,和相应的控制器,设计数据库表,

3.设计路由,设计好所有可能的接口。

4.处理身份认证的中间件。

5.验证码服务

1.短信验证码

阿里云通讯、容联云通讯

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tNUsFIh-1660871673258)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220317090023479.png)]

2.邮箱验证码

smtp.163.com邮箱

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3wcGFM0-1660871673259)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220317090607464.png)]

忘记密码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GUVkBWHn-1660871673260)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220317090855435.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngKYSbXF-1660871673261)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220317090931873.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzslm8Bi-1660871673262)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20220317091200263.png)]

RBAC基于权限的访问控制。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CBvPKiTL-1660871673281)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211212220709605.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9w3gvFzq-1660871673282)(C:\Users\Icy-yun\AppData\Roaming\Typora\typora-user-images\image-20211212222207660.png)]

1.云通讯 进行手机号的注册

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值