热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

模子,保留数据到数据库

文章泉源:模子,保留数据到数据库环境搭建以及运用Ember.js建立第一个静态页面引入盘算属性、action、动态内容继承为读者引见怎样运用Ember构建一个完全的、庞杂的项目。第

文章泉源:模子,保留数据到数据库

  1. 环境搭建以及运用Ember.js建立第一个静态页面
  2. 引入盘算属性、action、动态内容

继承为读者引见怎样运用Ember构建一个完全的、庞杂的项目。

第一个Ember.js模子

在前面两篇中完成了怎样猎取界面输入的邮箱值,然则并没有真正保留到数据,仅仅只是猎取界面输入的值并显现出来。在本篇中将为读者演示怎样保留数据到数据库中。然则我并不会去建立一个数据库,而是运用firebase,更多有关firebase的信息请自行查阅材料进修(假如接见firebase官网很慢或许是没法接见那末你须要fanqiang)!

言归正传,回到Ember的模子引见中来。简朴讲Ember的模子实在就是一个与数据表对应的一个实体类,与Java中的JavaBean有点相似。
建立一个模子也异常简朴,能够直接运用Ember CLI敕令建立,下面的敕令就是用于建立模子类,并在模子中增添一个string范例的属性email

ember g model invitation email:string

敕令实行终了以后能够在项目对应目录下看到建立的文件app/models/invitaction.js,文件内如以下:

// app/models/invitation.js
import DS from 'ember-data';
export default DS.Model.extend({
email: DS.attr('string')
});

有了模子类以后修正控制器index.js的代码,到场模子,经由历程模子来保留数据对象。

// app/controller/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
headerMessage: 'Coming Soon',
responseMessage: '', // 设置默认值为空字符串
emailAddress: '', // 设置默认值为空字符串
// 运用正则表达式推断邮箱花样,假如准确则返回true反之返回false
isValid: Ember.computed.match('emailAddress', /^.+@.+\..+$/),
// 把盘算属性isValid绑定到isDisabled上
isDisabled: Ember.computed.not('isValid'), //当`disabled=false`时按钮可用,所以恰好须要取反
actions: {
saveInvitation: function() {
const email = this.get('emailAddress');
// 建立一个模子对象
const newInvitaction = this.store.createRecord('invitation', { email: email });
newInvitaction.save(); //保留模子对象到store中
this.set('responseMessage', `Thank you! We've just saved your email address: ${this.get('emailAddress')}`);
// 状况输入框内容
this.set('emailAddress', '');
}
}
});

守候项目重新启动,在界面输入准确的邮箱,点击按钮,能够在浏览器控制台看到以下毛病信息:

《模子,保留数据到数据库》
图1

config/environment.js设置代码以下:

var ENV = {
modulePrefix: 'library-app',
environment: environment,
contentSecurityPolicy: { 'connect-src': "'self' https://auth.firebase.com wss://*.firebaseio.com" },
firebase: 'https://YOUR-FIREBASE-NAME.firebaseio.com/', //改成本身在firebase上APP的地点
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
}
};
// ……其他代码省略

注重上述代码中的第5行,firebase属性的值是本身在firebase请求的APP的URL。一定记得要修正!!!

修正完成以后手动重启项目,记得是手动封闭终端运转的项目(ctrl+c封闭),然后再运用敕令ember s启动项目。不然新装置的emberfire没法起作用。

守候项目启动完成,假如启动历程当中没有涌现毛病,申明emberfire装置胜利!

然后冲动的时刻到了,在首页输入准确的邮箱,点击按钮,能够看到浏览器控制来不会报错了!而且在firebase官网的APP中看到方才新增的邮箱!!

注重:点击按钮提交后能够看到界面没有任何回响反映,先别急,由于firebase是外国的东西,在天朝接见都是比较慢,你懂的。提交后到相应返来能够比较慢。

从浏览器控制台打印的日记能够看出向firebase发送请求,截图以下:

《模子,保留数据到数据库》

而且在界面上提醒了保留胜利的信息!

末了在firebase官网上能够检察到方才提交数据。

《模子,保留数据到数据库》

能够感受到firebase的壮大了吧!我们险些没有做任何处置惩罚数据就直接保留到firebase了,而且会自动依据模子建立数据,不过须要注重的是我们在模子定义中并不须要定义id属性,firebase会自动天生一个唯一的id属性值,截图中的-KEr3XwUQjgLjb5yx0dp就是id属性值。

到此,数据的保留事情完成了,借助firebase大大简化了本身须要处置惩罚的东西,不须要本身建立数据库、数据表、以及保留数据sql等等!不知道你是不是看邃晓了,假若有疑问请实时给我留言,我会全力为你解答!

promise和this

promise(许诺)在Javascript中是一个异步特征。这个特征还在完美当中,更多有关promise的引见请看promises-book或许Mozilla MDN Promise。

在前面保留数据的代码中save()要领返回值就是一个promise,我们能够依据save()要领的返回值做差别的处置惩罚,比方保留失利时刻的处置惩罚。

saveInvitation: function() {
const email = this.get('emailAddress');
// 建立一个模子对象
const newInvitaction = this.store.createRecord('invitation', { email: email });
//保留模子对象到store中
newInvitaction.save().then(function(msg) {
console.log('保留胜利。');
}, function(reason) {
console.log('保留失利!');
});
this.set('responseMessage', `Thank you! We've just saved your email address: ${this.get('emailAddress')}`);
// 状况输入框内容
this.set('emailAddress', '');
}

假如你看过有关promise的引见那末明白上述代码应该是很简朴的,在要领then()中第一个函数(参数)会在save()实行胜利的时刻实行,第二个函数(参数)会在save()实行失利的时刻实行。邃晓这个以后我们再修正控制器index.js的代码。我们把提醒信息放在save()实行胜利的时刻实行要领中。

saveInvitation: function() {
const email = this.get('emailAddress');
// 建立一个模子对象
const newInvitaction = this.store.createRecord('invitation', { email: email });
//保留模子对象到store中
newInvitaction.save().then(function(msg) {
this.set('responseMessage', `Thank you! We've just saved your email address: ${this.get('emailAddress')}`);
// 状况输入框内容
this.set('emailAddress', '');
}, function(reason) {
this.set('responseMessage', `Saved: ${this.get('emailAddress')} failed!`);
// 状况输入框内容
this.set('emailAddress', '');
});
}

守候项目自动重启完成,在界面输入准确邮箱,提交数据,此时并没有涌现任何回响反映,而且会在浏览器控制台看到以下毛病,

《模子,保留数据到数据库》

这又是什么缘由呢?实在缘由很简朴,由于this作用域题目,由因而在then()内部运用了this致使此时的this指向的并非控制器类了,只要在Ember的高低文中才运用set()要领!我们用一个暂时变量处理这个题目,代码修正为以下:

// app/controller/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
headerMessage: 'Coming Soon',
responseMessage: '', // 设置默认值为空字符串
emailAddress: '', // 设置默认值为空字符串
// 运用正则表达式推断邮箱花样,假如准确则返回true反之返回false
isValid: Ember.computed.match('emailAddress', /^.+@.+\..+$/),
// 把盘算属性isValid绑定到isDisabled上
isDisabled: Ember.computed.not('isValid'), //当`disabled=false`时按钮可用,所以恰好须要取反
actions: {
saveInvitation: function() {
const email = this.get('emailAddress');
// 建立一个模子对象
const newInvitaction = this.store.createRecord('invitation', { email: email });
var _this = this;
//保留模子对象到store中
newInvitaction.save().then(function(msg) {
_this.set('responseMessage', `Thank you! We've just saved your email address: ${_this.get('emailAddress')}`);
// 状况输入框内容
_this.set('emailAddress', '');
}, function(reason) {
_this.set('responseMessage', `Saved: ${_this.get('emailAddress')} failed!`);
// 状况输入框内容
_this.set('emailAddress', '');
});
}
}
});

守候项目自动重启完成,在页面输入准确的邮箱并提交,能够看到此时结果与之前是一样的,然后去firebase检察结果,也是能够看到新增的数据。

虽然是用暂时变量体式格局能够处理由于this作用域题目,然则另有越发幽美的处理办法,现在险些一切新版的浏览器引擎已支撑ES2015,能够运用ES2015的=>操纵符处理this作用域题目,请看下面的处置惩罚代码:

saveInvitation: function() {
const email = this.get('emailAddress');
// 建立一个模子对象
const newInvitaction = this.store.createRecord('invitation', { email: email }); //保留模子对象到store中
newInvitaction.save().then((response) => {
console.log('respOnse= ' + response);
this.set('responseMessage', `Thank you! We've just saved your email address: ${response.get('id')}`);
// 状况输入框内容
this.set('emailAddress', '');
}, (reason) => {
this.set('responseMessage', `Saved: ${this.get('emailAddress')} failed!`);
// 状况输入框内容
this.set('emailAddress', '');
});
}

运用ES2015的特征以后不仅处理了this作用域题目,而且连关键字function都不须要了,运用=>操纵会自动把外层this所指的对象通报到函数内部,而且修正了保留胜利时的提醒信息,运用${response.get('id')}从firebase相应的数据中猎取到保留胜利后返回的id值,返回的response就是一个模子invitation的对象,能够运用get()要领猎取对象值。
再次测试,假如项目代码没有误那末你能够获得以下截图的提醒信息(id值跟你的是不一样的),

《模子,保留数据到数据库》

假如你对this不是很懂,请看仔细看下面文章的诠释:

  1. Mozilla MDN this
  2. Javascript的this用法

建立治理页面

前面已引见了怎样整合firebase到项目中,而且已胜利保留增添的数据。能够在firebase上看到一切数据,我们建立一个背景页面去治理这些数据。

下面建立一个子路由和路由对应的模板页面,仍然是运用Ember CLI敕令建立,敕令以下:

ember g route admin/invitaction

敕令实行终了后会获得一个路由文件(app/routes/admin/invitaction.js)和一个模板文件(app/templates/admin/invitaction.hbs),敕令会自动建立文件夹admin,子路由和子模板会放在子子目录下。
然后在首页增添菜单链接,修正navbar.hbs模板。


代码{{#link-to 'admin.invitation' tagName="li"}}Invitations{{/link-to}}admin.invitation是一个嵌套路由或许说是子路由。更多有关路由嵌套题目请看Ember.js 入门指南之十三{{link-to}} 助手。

在模板中运用表格遍历显现一切的邮箱数据。修正模板invitaction.hbs


Invitations










{{#each model as |invitation|}}




{{/each}}

ID E-mail
{{invitation.id}} {{invitation.email}}

上述代码中{{#each}}{{/each}}是Ember供应的遍历表达式,此表达式用于遍历数组数据。本例子中用户遍历从路由的model回调中返回的数据。更多有关此表达式的引见请看Ember.js 入门指南之十handlebars遍历标签。
修正路由app/routes/admin/invitations.jsmodel回调中猎取服务器(firebase)上的数据。

// app/routes/admin/invitations.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('invitation');
}
});

守候项目重启完成,能够在项目首页导航栏的右边看到能够点击下拉的Admin菜单项,点击菜单看到子菜单项“Invitation”,点击“Invitation”进入到http://localhost:4200/admin/invitation。
在界面上能够看到之前新增的一切邮箱信息和firebase自动天生的ID属性值(由于firebase是老外的东西猎取数据会比较慢,数据显现天然也会比较慢,稍等一会就在界面上看到了!)。假如你项目代码没题目也能够看到以下的截图。

《模子,保留数据到数据库》

数据的CRUD操纵

到这一步我们已能够完全的从服务器猎取数据,并以列表情势显现在界面上。本教程的目的是建立一个简朴的图书治理体系,前面的文章已完成了相似于用户注册的功用,接下来我们建立一个library模子,用于保留书库信息。
同样是运用Ember CLI敕令建立模子。

ember g model library name:string address:string phone:string

上述敕令建立了一个包含三个属性的模子,这三个属性都是string范例的数据。建立完模子以后再继承建立三个模板,离别用户显现library列表新建library数据。

ember g template libraries
ember g template libraries/index
ember g template libraries/new

模板建立终了以后手动在router.js中增添路由设置,此次我们不采纳Ember CLI敕令建立了!!!

// app/router.js
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('about');
this.route('contact');
this.route('admin', function() {
this.route('invitation');
});
this.route('libraries', function() {
this.route('new');
});
});
export default Router;

再更新首页模板navbar.hbs,增添一个菜单项“libraries”,其他代码稳定。

修正模板libraries.hbs,增添菜单链接。


Libraries






{{outlet}}

守候项目重启完成,进入http://localhost:4200/libraries。能够看到以下图的界面

《模子,保留数据到数据库》

此时点击界面的上的“List all”和“Add new”除了看到URL变化以外还没任何结果,由于我们的子模板libraries/index.hbslibraries/new.hbs还没有任何内容,下面在这两个模板中增添一些代码。


List


{{#each model as |library|}}


{{library.name}}




Address: {{library.address}}


Phone: {{library.phone}}




{{/each}}


Add a new local Library






{{input type="text" value=model.name class="form-control" placeholder="The name of the Library"}}





{{input type="text" value=model.address class="form-control" placeholder="The address of the Library"}}





{{input type="text" value=model.phone class="form-control" placeholder="The phone number of the Library"}}







模板new.hbs是一个表单,用于新增数据,经由历程点击按钮“Add to library list”提交表单数据,表单数据由路由libraries/new.js中的saveLibrary要领处置惩罚,此时此要领还没定义,在接下来的代码中会定义。
{{action}}表达式中通报了一个参数model到处置惩罚的背景,表单中的其他属性会以model的属性体式格局通报到背景,之所以能够如许做是由于在模板对应的路由中返回了一个空的library对象,在接下来的路由libraries/new.js将看到。
守候项目重启完,在点击“List all”和“Add new”能够看到这两个子模板的内容衬着到父模板libraries.hbs{{outlet}}上。不过由于并没有在路由中猎取模子library的数据所以“List all”页面还没有任何数据,“Add new”页面是第一个新增数据吧表单。
下面在路由libraries/index.js中猎取library的数据,并在model回调中返回到模板中遍历显现。
运用Ember CLI敕令建立路由,建立历程会讯问是不是掩盖已存在的模板文件,输入n挑选否。

ember g route libraries/index
ember g route libraries/new

路由建立完成以后离别在这两个路由中增添猎取数据的代码。

// app/routes/libraries/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('library');
}
});

// app/routes/libraries/new.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.createRecord('library');
},
actions: {
// 处置惩罚模板上输入的数据
saveLibrary(newLibrary) {
newLibrary.save().then(() => this.transitionTo('libraries'));
},
willTransition() {
// rollbackAttributes() removes the record from the store
// if the model 'isNew'
this.controller.get('model').rollbackAttributes();
}
}
});

在此路由的model回调中我们建立了一个空的library对象,并返回到模板页面。这也是为什么能在模板中通报参数model的缘由。
代码中要领willTransition()是Ember供应的内置要领,此要领的作用是当用户脱离当前URL时会清空未保留到服务器的library数据,假如不重置modelEmber会在路由切换的时刻自动保留数据到服务器上。
保留数据的要领saveLibrary()写的比较简约,它的作用是:先挪用save()要领保留数据,假如保留胜利在挪用要领transitionTo()跳转到路由libraries下(library首页),有关=>语法的引见请看Mozill MDN Arrow_functions。
在上述代码中屡次是用了this.controller,然则在路由中并没有这个属性controller而且也没有控制器文件app/controllers/libraries/new.js,运转项目并不会报错!这是为什么呢?这是由于Ember会自动天生一个假造的控制器并在保留在内存中,依据Ember的定名规则会自动天生一个与路由同名的控制器,
为了考证这个说法,翻开浏览器的控制台,在翻开标签“Ember”然后点击左边的“/# Routes”,找到路由libraries这一块,能够看到以下截图的信息。

《模子,保留数据到数据库》

能够看到每一个路由都对应着一个同名的控制器。

守候项目重启终了,最先考证代码是不是完成了所想象的请求。
首先在新增页面输入以下截图信息,然后点击按钮保留数据。

《模子,保留数据到数据库》

稍等片刻,守候数据保留终了,能够看到界面顺遂跳转到了http://localhost:4200/libraries下,以下图所示,而且看到了方才新增的数据,为了考证数据是不是真的保留到服务器中,我们进入到firebase的APP中检察,能够看到数据以及保留到里library下。

《模子,保留数据到数据库》

library数据列表页面截图

《模子,保留数据到数据库》

firebase上保留的library数据截图

此时,假如你解释了要领willTransition()结果会是怎样的呢!!假如没有这个要领去重置model,当你每次在“Add new”页面输入输入而且没有点击“Add to library list”保留数,然后切换到其他路由下(比方点击“List all”切换到路由libraries下)会自动保留一条数据到服务器。

  • 在“Add new”页面输入以下截图数据

《模子,保留数据到数据库》

  • 点击按钮“List all”切换到路由libraries下,能够看到在“Add new”页面增加的数据,以下图所示,然则假如你手动革新页面后能够发明这条数据不见了,而且在firebase上也没有这条数据,可见这条数据仅仅是保留到Ember的store中,并没有真正保留到服务器上。如许的体验是异常蹩脚的!!

《模子,保留数据到数据库》

个中,完成重置model的体式格局另有别的一种越发适宜的要领,代码以下:

willTransition() {
// rollbackAttributes() removes the record from the store
// if the model 'isNew'
// this.controller.get('model').rollbackAttributes();
let model = this.controller.get('model');
if (model.get('isNew')) {
model.destroyRecord();
}
}

本篇的内容到此悉数引见终了!本篇我们完成了数据的保留、显现,特别是library数据的保留。数据的保留、显现都须要与模子关联,模子在Ember是一个异常重要的内容!愿望读者好好控制模子。

家庭功课

下面两个功课完成个中之一即可。本篇挑选第一个,第二个请读者自行完成!着手才是真谛。

加强contact的功用

  1. 建立一个包含两个属性emailmessage的模子contact,参考代码
  2. 修正路由app/routes/contact.js返回一个空对象到模板上,参考代码
  3. 修正控制器contact.js,保留数据到firebase,参考代码
  4. 把放在控制器中的校验代码移动到模子app/models/contact.js中,参考代码
  5. 建立一个治理contact的背景页面http://localhost:4200/admin/contacts,参考代码
  6. 在项目首页的导航菜单上增添一个菜单项“Contacts”,点击菜单进入http://localhost:4200/admin/contact,参考代码
  7. 运用表格展现一切的contact数据,参考代码

运用路由和模子重构有关contact的代码

  1. 把有关contact的磨练放到模子类中
  2. 把控制器contact.js中保留数据的代码移动到同名的路由中
  3. 测试经由历程后删除控制器contact.js

功课演示结果

《模子,保留数据到数据库》

当输入的邮箱花样准确,而且message长度大于6时按钮“send”才可用。

《模子,保留数据到数据库》

保留胜利后状况输入框,而且显现提醒信息。当切换路由后再进入到http://localhost:4200/contact会清空保留胜利的提醒信息。

《模子,保留数据到数据库》

背景页面胜利显现了新增的数据,纵然手动革新页面数据也不会丧失,可见数据已保留到firebase,在此不再贴firebase上的数据截图了!

为了照应懒人我把完全的代码放在GitHub上,若有须要请参考参考。博文经由屡次修正,博文上的代码与github代码能够有相差,不过影响不大!假如你以为博文对你有点用,请在github项目上给我点个star吧。您的一定对我来说是最大的动力!!


推荐阅读
  • 导出功能protectedvoidbtnExport(objectsender,EventArgse){用来打开下载窗口stringfileName中 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
author-avatar
mumei4_839_210
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有