在本教程中,我们将为iOS创建一个类似于Tinder的约会应用程序。 对于语音和消息传递,我们将利用Sinch平台,利用其强大的SDK。
在第一部分中,我们将专注于RESTful API的开发,以存储和检索用户信息。 在第二部分中,iOS客户端将挂接到该API中,以根据用户的当前位置查找附近的用户。
我们将把Laravel 5.0用于RESTful服务,并将涵盖基本概念,例如路由和控制器。 我们还将定义自定义模型,以类似于ActiveRecord的方式支持MongoDB集成。 让我们开始吧。
1.基本设置
我假设您已经安装了Composer和最新的Laravel安装程序 。 如果还没有,请遵循Laravel 5.0官方文档 。 安装过程不应超过几分钟。
从命令行,导航到要为RESTful服务创建应用程序的位置,然后执行以下命令:
laravel new mobilesinch
几秒钟后,该命令将告诉您mobilesinch应用程序已成功创建。 导航到新文件夹并执行以下命令:
php artisan fresh
默认情况下,任何Laravel 5.0应用程序都附带一些用于用户注册和身份验证的基本支架。 由于我们要从头开始,因此此命令将删除此命令。
在为RESTful服务编写实际代码之前,我们还需要处理另一件事。 默认情况下,Laravel 5.0附带了用于跨站点请求伪造(CSRF)保护的中间件。 但是,由于我们不是在构建网站而是在建立RESTful API,因此没有必要使用它。 同样,它可能会在使用过程中引起一些问题。
对于此应用程序,最好将其删除。 在应用程序文件夹的根目录中,导航到app / Http 。 在该文件夹中,有一个名为Kernel.php的文件。 打开它并删除以下行:
'App\Http\Middleware\VerifyCsrfToken',
您还可以删除位于app / Http / Controllers内的WelcomeController.php ,以及位于resources / views文件夹内的默认welcome.blade.php视图。 我们不会使用它们,但是您可以根据需要将其保留在此处。 只需确保将503.blade.php视图保留在适当的位置即可,这对于调试应用程序很有用。
2. Base
模型
本教程旨在创建的约会应用程序具有类似Tinder的功能,您可以在其中找到当前位置附近的用户。 为此,API需要根据用户的位置执行搜索,这称为地理空间查询。 尽管我们可以使用MySQL做到这一点,但是行业标准倾向于MongoDB,我个人更喜欢它。
代替使用Laravel的DB Facade,我们将创建自己的类,应用程序的模型将扩展为在MongoDB中执行查询。
这将是一个简单的类,并且不会集成到Laravel的Eloquent模型中,尽管我们可以,但我想暂时保持简单。
步骤1:MongoDB配置
在编写用于执行MongoDB查询的类之前,我们需要设置数据库信息,就像对MySQL或PostgreSQL或任何其他数据库服务器所做的一样。
在根config文件夹中,创建一个新文件,并将其命名为mongodb.php 。 向其添加以下代码:
<?php
return [
'host' => 'localhost',
'port' => 27017,
'user' => '',
'pass' => '',
'db' => 'mobilesinch'
];
我们为MongoDB服务器设置主机和端口(如果有),为连接设置用户和密码,并定义我们将使用的数据库mobilesinch 。
由于MongoDB是一个面向文档的数据库,它是无模式的,因此我们不需要进一步的配置,迁移定义或其他任何结构表。 它只是工作。
步骤2:数据库连接
我们已经有了配置文件,现在该创建用于处理与数据库交互的实际类了。
此类将使用类似ActiveRecord的语法执行对MongoDB的查询。 在app / Http内部 文件夹,创建一个新的Models ,并在其中添加Base.php文件。 为其添加以下代码:
<?php namespace App\Http\Models;
use Illuminate\Support\Facades\Config;
class Base {
private $_config = null;
private $_conn = null;
private $_db = null;
public function __construct() {
$this->_config = Config::get( 'mongodb' );
$this->_connect();
}
private function _connect() {}
}
这些是我们Base
模型类的Base
。 它不扩展任何内容,仅依靠Laravel的Config
外观来检索我们之前创建的配置参数。
接下来,我们需要创建与数据库的连接。 将以下代码添加到private _connect
方法:
$conn = 'mongodb://'.$this->_config['host'];
if( ! empty( $this->_config['port'] ) ) {
$conn .= ":{$this->_config['port']}";
}
$options = array();
if( ! empty( $this->_config['user'] ) && ! empty( $this->_config['pass'] ) ) {
$options['username'] = $this->_config['user'];
$options['password'] = $this->_config['pass'];
}
try {
$this->_conn = new \MongoClient( $conn, $options );
$this->_db = $this->_conn->{$this->_config['db']};
return true;
} catch( \MongoConnectionException $e ) {
$this->_conn = null;
return false;
}
在这种方法中,我们创建一个连接字符串,并设置用户名和密码(如果有的话)。 然后,我们使用PHP的MongoDB驱动程序创建一个连接,并将数据库设置为配置文件中指定的数据库。
如果您从命令行熟悉MongoDB语法,则此方法等效于进入mongo控制台并键入use mobilesinch
。 有关更多信息,请参考PHP MongoDB官方文档 。
步骤3:辅助方法
在继续数据库CRUD操作之前,我们的Base
类必须实现一些方法。 这些用于设置过滤器,选择语句和其他用于执行数据库操作的查询变量。 让我们从添加必要的成员变量开始。 在类构造函数上方,添加以下代码:
private $_ws = array();
private $_sls = array();
private $_lmt = 99999;
private $_ost = 0;
这些是数据库查询的where , select , limit和offset的持有者。 要设置这些成员变量,请创建以下setter方法:
protected function _limit( $limit, $offset = null ) {}
protected function _select( $select = "" ) {}
protected function _where( $key, $value = null ) {}
_limit
方法对于分页READ操作的结果很有用。 用户可以设置limit
参数来指定要检索的记录数,还可以设置offset
参数来指定要读取的页面。 将以下代码添加到_limit
方法中:
if ( $limit !== NULL && is_numeric( $limit ) && $limit >= 1 ) {
$this->_lmt = $limit;
}
if ( $offset !== NULL && is_numeric( $offset ) && $offset >= 1 ) {
$this->_ost = $offset;
}
_select
方法将用于确定READ查询必须返回记录的哪些字段。 select语句必须以逗号分隔的字符串形式提供。
$fields = explode( ',', $select );
foreach ( $fields as $field ) {
$this->_sls[trim( $field )] = true;
}
最后, _where
方法将用于过滤查询结果,可以是数组或键/值对。
if ( is_array( $key ) ) {
foreach( $key as $k => $v ) {
$this->_ws[$k] = $v;
}
} else {
$this->_ws[$key] = $value;
}
现在,我们已经支持限制和过滤查询,但是我们必须添加其他一些辅助方法。 第一个将用于在发出查询之前将任何where语句集与查询的where参数组合在一起。
稍后,当我们编写CRUD方法时,这会更有意义。 在类的底部,添加以下私有方法:
private function _set_where( $where = null ) {
if ( is_array( $where ) ) {
$where = array_merge( $where, $this->_ws );
foreach ( $where as $k => $v ) {
if ( $k == "_id" && ( gettype( $v ) == "string" ) ) {
$this->_ws[$k] = new \MongoId( $v );
} else {
$this->_ws[$k] = $v;
}
}
} else if( is_string( $where ) ) {
$wheres = explode( ',', $where );
foreach ( $wheres as $wr ) {
$pair = explode( '=', trim( $wr ) );
if ( $pair[0] == "_id" ) {
$this->_ws[trim( $pair[0] )] = new \MongoId( trim( $pair[1] ) );
} else {
$this->_ws[trim( $pair[0] )] = trim( $pair[1] );
}
}
}
}
看起来有些吓人,但实际上非常简单。 它首先检查where
参数是否为数组。 如果是这样,它将使用_where
helper方法将给定值与现有值合并。
但是,此方法还支持用于设置READ操作返回值的字符串。 该字符串应具有以下格式:
name=John,last_name=Smith
本示例将运行查询,并返回将name
字段设置为John
并将last_name
字段设置为Smith
字段。
但是请注意,对于数组或字符串,我们都会检查_id
字段是否存在。 如果是这种情况,它是一个字符串,我们将根据它创建一个新的MongoId
对象。 id是MongoDB中的对象,将它们与字符串进行比较将返回false
,这就是为什么必须进行此转换的原因。
我们要做的另一件事是,一旦执行了一个操作,就重置所有查询参数,这样它们就不会影响后续查询。 _flush
方法将解决此问题。
private function _flush() {
$this->_ws = array();
$this->_sls = array();
$this->_lmt = 99999;
$this->_ost = 0;
}
步骤4:CRUD操作
现在,我们具有所有必需的功能,可以过滤和限制查询结果。 现在该进行实际的数据库操作了,这将取决于PHP的MongoDB驱动程序。 如果不确定某事,请参阅文档 。
创建操作
我们要支持的第一个操作是在系统中创建记录的操作。 将以下代码添加到Base
类:
protected function _insert( $collection, $data ) {
if ( is_object( $data ) ) {
$data = ( array ) $data;
}
$result = false;
try {
if ( $this->_db->{$collection}->insert( $data ) ) {
$data['_id'] = ( string ) $data['_id'];
$result = ( object ) $data;
}
} catch( \MongoCursorException $e ) {
$result = new \stdClass();
$result->error = $e->getMessage();
}
$this->_flush();
return $result;
}
即使PHP驱动程序期望插入的数据是一个数组,我们的类也将同时支持数组和对象。 我们首先验证传递给我们的内容,然后进行相应的投射。 然后,我们尝试将记录插入数据库中,并将插入的记录作为对象返回,包括_id
。
读操作
我们将实现两种读取方法,一种将用于检索单个记录,而另一种将用于获取记录列表。 让我们从前者开始。
protected function _findOne( $collection, $where = array() ) {
$this->_set_where( $where );
$row = $this->_db->{$collection}->findOne( $this->_ws, $this->_sls );
$this->_flush();
return ( object ) $row;
}
我们为查询定义where子句,并使用PHP的MongoDB驱动程序执行findOne
操作。 然后,我们刷新查询参数并将记录作为对象返回。
PHP的MongoDB驱动程序以数组形式返回结果,而我个人更喜欢对象。 那是演员阵容的真正原因。
接下来,我们实现_find
方法以获取记录列表。
protected function _find( $collection, $where = array() ) {
$this->_set_where( $where );
$docs = $this->_db->{$collection}
->find( $this->_ws, $this->_sls )
->limit( $this->_lmt )
->skip( $this->_ost );
$this->_flush();
$result = array();
foreach( $docs as $row ) {
$result[] = ( object ) $row;
}
return $result;
}
在_find
方法中,我们使用驱动程序的find
方法,设置查询limit
并skip
参数以支持分页。
但是,此方法返回一个MongoCursor
对象,然后我们对其进行迭代以获取实际记录。 和以前一样,我们将每个记录都转换为对象,并将其追加到结果数组。
UPDATE操作
我们已经支持从数据库创建和读取记录。 现在,我们需要能够编辑这些记录并添加或修改记录的数据。 创建一个新方法_update
并实现它,如下所示:
protected function _update( $collection, $data, $where = array() ) {
if ( is_object( $data ) ) {
$data = ( array ) $data;
}
$this->_set_where( $where );
if ( array_key_exists( '$set', $data ) ) {
$newdoc = $data;
} else {
$newdoc = array( '$set' => $data );
}
$result = false;
try {
if( $this->_db->{$collection}->update( $this->_ws, $newdoc ) ) {
$result = ( object ) $data;
}
} catch( \MongoCursorException $e ) {
$result = new \stdClass();
$result->error = $e->getMessage();
}
$this->_flush();
return $result;
}
与CREATE操作一样,我们支持数组和对象,这意味着我们将进行相应的检查和转换。 我们使用辅助方法将传递给该方法的where子句组合在一起。 其余与已经创建的_insert
方法没有什么不同。
不过,有一件特别的事情要注意。 当我们更新MongoDB文档并将数据传递给_update
方法时,该文档将被替换。 如果我们仅更新一个字段并传递该字段的数据,则文档将成为该字段。 这就是为什么我们需要使用$set
键和添加的信息创建一个数组。 结果是我们的记录不会被新信息代替。
删除操作
最后,驱动程序必须支持DELETE操作才能从数据库中删除文档。
protected function _remove( $collection, $where = array() ) {
$this->_set_where( $where );
$result = false;
try {
if ( $this->_db->{$collection}->remove( $this->_ws ) ) {
$result = true;
}
} catch( \MongoCursorException $e ) {
$result = new \stdClass();
$result->error = $e->getMessage();
}
$this->_flush();
return $result;
}
和以前一样,我们为删除操作设置where子句,并依靠PHP的MongoDB驱动程序对数据库执行remove
操作。
这就是我们的Base
模型。 它有很多代码,但是我们现在可以在MongoDB中对继承自Base
类的模型执行操作。
3. Session
模型
Session
模型将负责在数据库中创建,删除和查找会话。 在应用程序的Models文件夹中创建一个新文件,将其命名为Session.php ,并向其中添加以下代码:
<?php namespace App\Http\Models;
class Session extends Base {
private $_col = "sessions";
public function create( $user ) {
$this->_where( 'user_id', ( string ) $user->_id );
$existing = $this->_findOne( $this->_col );
if ( !empty( ( array ) $existing ) ) {
$this->_where( 'user_id', ( string ) $user->_id );
$this->_remove( $this->_col );
}
$session = new \stdClass();
$session->user_id = ( string ) $user->_id;
$session->user_name = $user->name;
$session = $this->_insert( $this->_col, $session );
return $session;
}
public function find( $token ) {
$this->_where( '_id', $token );
return $this->_findOne( $this->_col );
}
public function remove( $token ) {
$this->_where( '_id', $token );
return $this->_remove( $this->_col );
}
}
该模型是我们先前创建的支持MongoDB操作的Base
类的扩展。 它还设置了用于sessions
的集合。
create
方法用于创建用户会话记录。 在尝试创建它之前,该方法将验证用户是否已经具有活动会话。 如果是这样,它将从数据库中删除它,并使用传入的用户信息创建新记录。
find
方法用于使用会话令牌从数据库中检索会话记录。 请注意,它只是为查询设置了where子句,并将查找记录的任务委托给Base
类的_findOne
方法。
为了结束用户会话,我们实现了remove
方法。 使用会话令牌,它将繁重的工作委托给Base
类的_remove
方法。 请注意,该模型不检查传入的会话令牌。这应由控制器处理。 该模型唯一需要关注的是数据操作。
4. User
模型
REST API需要的另一种模型是处理与用户相关的交互的模型。 在应用程序的Models文件夹中,创建一个新的User.php文件,并向其中添加以下代码:
<?php namespace App\Http\Models;
use App\Http\Models\Base as Model;
class User extends Model {
private $_col = "users";
private $_error = null;
public function get( $where ) {}
public function get_error() {}
public function create( $user ) {}
public function remove( $id ) {}
public function retrieve( $id, $distance, $limit = 9999, $page = 1 ) {}
public function update( $id, $data ) {}
}
User
模型稍微复杂一些。 让我们从检索用户的方法开始。 get
方法将负责使用用户的ID检索单个记录。 将以下代码添加到get
方法:
if ( is_array( $where ) ) {
return $this->_findOne( $this->_col, $where );
} else {
$this->_where( '_id', $where );
return $this->_findOne( $this->_col );
}
我们假设在where
参数不是数组的情况下,它是用户的ID。 然后, get
方法将查找记录的任务委托给Base
类的_findOne
方法。
的 get_error
方法是一个辅助方法,它将为控制器提供有关模型故障的更多信息。
return $this->_error;
User
模型中的最后一个读取操作是针对retrieve
方法的操作。 这将获取用户列表。 将以下代码添加到retrieve
方法中:
if ( !empty( $id ) && !empty( $distance ) ) {
$this->_where( '_id', $id );
$this->_select( 'location' );
$user = $this->_findOne( $this->_col );
if ( empty( ( array ) $user ) ) {
$this->_error = "ERROR_INVALID_USER";
return false;
}
$this->_where( '$and', array(
array(
'_id' => array( '$ne' => new \MongoId( $id ) )
),
array(
'location' => array(
'$nearSphere' => array(
'$geometry' => array(
'type' => "Point",
'coordinates' => $user->location['coordinates']
),
'$maxDistance' => ( float ) $distance
)
)
)
) );
}
$this->_limit( $limit, ( $limit * --$page ) );
return $this->_find( $this->_col );
此方法支持分页和地理空间查询。 如果传入了id
和distance
参数,它将尝试根据用户的位置搜索附近的用户。
如果id
与任何记录都不匹配,则返回false
。 如果用户确实存在,它将使用MongoDB 2dsphere索引准备地理空间查询。
请注意,我们还将查询设置为不返回与执行搜索的用户的_id
相匹配的用户。 最后,它设置查询限制和偏移量参数,从而将任务委派给Base
类的_find
方法。
要删除用户,我们需要实现remove
方法。 将以下代码添加到remove
方法:
$this->_where( '_id', $id );
$user = $this->_findOne( $this->_col );
if ( empty( ( array ) $user ) ) {
$this->_error = "ERROR_INVALID_ID";
return false;
} else {
$this->_where( '_id', $id );
if ( !$this->_remove( $this->_col ) ) {
$this->_error = "ERROR_REMOVING_USER";
return false;
}
}
return $user;
我们检查给定的_id
对应于现有用户,并尝试使用Base
类的_remove
方法将其删除。 如果出现问题,我们将设置模型的_error
属性并返回false
。
我们的模型应支持的另一种操作是创建用户记录。 将以下代码添加到create
方法:
if ( is_array( $user ) ) {
$user = ( object ) $user;
}
$this->_where( '$or', array(
array(
"email" => $user->email
),
array(
"mobile" => $user->mobile
)
)
);
$existing = $this->_findOne( $this->_col );
if ( empty( ( array ) $existing ) ) {
$user = $this->_insert( $this->_col, $user );
} else {
$user = $existing;
}
$user->_id = ( string ) $user->_id;
return $user;
在这种方法中,我们确保没有用户与给定的email
或mobile
相关联。 如果是这样,我们将返回相应的用户。 如果不是,我们将创建用户的任务委派给Base
类的_insert
方法。
在返回用户记录之前,我们将_id
转换为字符串。 这是为什么? 返回给我们的对象将_id
字段定义为MongoId对象。 但是,客户端应用程序不需要此对象。
User
模型还需要支持更新用户记录。 将以下代码添加到update
方法:
if ( is_array( $data ) ) {
$data = ( object ) $data;
}
if ( isset( $data->email ) || isset( $data->mobile ) ) {
$this->_where( '$and', array(
array(
'_id' => array( '$ne' => new \MongoId( $id ) )
),
array(
'$or' => array(
array(
'email' => ( isset( $data->email ) ) ? $data->email : ""
),
array(
'mobile' => ( isset( $data->mobile ) ) ? $data->mobile : ""
)
)
)
)
);
$existing = $this->_findOne( $this->_col );
if ( !empty( ( array ) $existing ) && $existing->_id != $id ) {
$this->_error = "ERROR_EXISTING_USER";
return false;
}
}
$this->_where( '_id', $id );
return $this->_update( $this->_col, ( array ) $data );
与在Base
类中一样, update
方法接受数组和对象作为用户的数据。 这使方法更加灵活。
在更新用户记录之前,请确保其他用户尚未使用该用户的email
和mobile
。 如果是这种情况,我们将错误设置为EXISTING_USER
并返回false
。 否则,我们将更新操作委托给Base
类。
5. BaseController
类
就像应用程序的模型继承自Base
类一样,控制器也继承自Laravel的Controller
类之外的公共父类。 但是,这个BaseController
类不会接近Base
模型的复杂性。
该类仅用于处理一些简单的任务。 要创建类,我们使用Laravel的artisan命令。 在命令行中,导航到应用程序的根目录并执行以下命令:
php artisan make:controller BaseController --plain
这将在app / Http文件夹中的应用程序的Controllers文件夹内创建一个名为BaseController.php的文件。 由于我们正在使用 --plain
标志,控制器将没有任何方法,这就是我们想要的。
该控制器将不会使用Request
类,因此可以继续删除以下行:
use Illuminate\Http\Request;
因为我们需要访问Session
模型,所以BaseController
添加到BaseController
类的声明中:
use App\Http\Models\Session as SessionModel;
现在,我们准备实现BaseController
类的方法。 首先在类声明中添加以下方法:
protected function _check_session( $token = "", $id = "" ) {
$result = false;
if ( !empty( $token ) ) {
$SessionModel = new SessionModel();
$session = $SessionModel->find( $token );
if ( !empty( ( array ) $session ) ) {
if ( !empty( $id ) ) {
if ( $session->user_id == $id ) {
$result = $session;
}
} else {
$result = $session;
}
}
}
return $result;
}
protected function _response( $result ) {
if ( is_object( $result ) && property_exists( $result, "status" ) ) {
return response()->json( $result, $result->status );
} else {
return response()->json( $result );
}
}
_check_session
方法用于验证会话令牌,该令牌作为第一个参数传递。 应用程序中的某些任务要求用户登录。例如,在更新用户记录时,与活动会话相对应的用户需要匹配需要更新的记录的_id
。
实现非常简单。 我们获取会话令牌的会话,如果与该会话相对应的用户的ID与作为第二个参数传递的ID匹配,则返回该会话。 否则,我们返回false
。
另一个帮助程序方法负责将结果发送回使用该API的客户端。 目前,我们仅支持JSON。 如果要返回的结果是一个对象并且具有状态参数,则可以使用Laravel的response
帮助器方法进行设置。 否则,我们仅返回结果。
6. SessionController
类
我们将实现的下一个控制器是处理对Sessions
资源的请求的控制器。 在命令行中,导航到应用程序的根目录并执行以下命令:
php artisan make:controller SessionController --plain
这将在app / Http文件夹中的应用程序的Controllers文件夹内创建一个名为SessionController.php的新文件。 在实施此类之前,我们需要注意一些事项。
SessionController
类当前继承自Laravel的Controller
类。 我们需要设置它以使用我们的BaseController
类。 这意味着我们需要更换
use App\Http\Controllers\Controller;
与
use App\Http\Controllers\BaseController;
我们还需要更改该类的extends
子句。 而不是从Controller
进行扩展,请确保您的类在扩展BaseController
类。 我们还需要包括控制器中使用的模型。 在最后一个声明下方,添加以下行:
use App\Http\Models\Session as SessionModel;
use App\Http\Models\User as UserModel;
通常,我们只使用SessionModel
,但是您很快就会看到为什么还要使用UserModel
原因。 至于控制器类本身,请添加以下代码:
private $_model = null;
public function __construct() {
$this->_model = new SessionModel();
}
public function create( Request $request ) {}
public function destroy( $token ) {}
我们在构造函数中设置控制器的model
对象,并声明几个方法,这是Sessions
资源支持的操作。
第1步:会话删除
为了删除用户会话,我们仅使用会话令牌,该令牌在资源URL中作为参数给出。 我们将在稍后的申请路线中对此进行声明。 在destroy
方法内,添加以下代码:
$result = new \stdClass();
if ( !$this->_model->remove( $token ) ) {
$result->error = "ERROR_REMOVING_SESSION";
$result->status = 403;
}
return $this->_response( $result );
该方法使用SessionModel
的remove
方法,并使用BaseController
类的_response
方法返回结果。 如果删除会话成功,我们将返回一个空对象。 如果发生错误,我们将返回带有403
状态代码的错误。
步骤2:建立工作阶段
创建会话的方法稍微复杂一些。 请注意,在方法声明中,我们使用的是Laravel的Request
对象。 我们使用该对象访问请求的POST参数。 在create
方法内,添加以下代码:
$email = $request->get( 'email' );
$mobile = $request->get( 'mobile' );
$fbId = $request->get( 'fbId' );
$result = new \stdClass();
if ( ( empty( $email ) && empty( $mobile ) ) || empty( $fbId ) ) {
$result->error = "ERROR_INVALID_PARAMETERS";
$result->status = 403;
} else {}
return $this->_response( $result );
我们尚未创建会话对象,因为首先需要讨论一些内容。 该应用程序将仅使用Facebook登录名。 在执行登录操作时,我们将从Facebook SDK中获取用户信息。 在API的Session
资源POST处理程序中,我们需要支持两件事:
- 为用户启动会话
- 在不存在时创建用户,然后开始会话
这也是在控制器中包含UserModel
的原因。 在上面的空else
子句中,添加以下代码:
$UserModel = new UserModel();
$where = ( !empty( $email ) ) ? array( 'email' => $email ) : array( 'mobile' => $mobile );
$user = $UserModel->get( $where );
if ( empty( ( array ) $user ) ) {
} else {
if ( $fbId != $user->fbId ) {
$result->error = "ERROR_INVALID_CREDENTIALS";
$result->status = 403;
}
}
if ( !property_exists( $result, "error" ) ) {
$result = $this->_model->create( $user );
$result->token = $result->_id;
unset( $result->_id );
}
我们首先使用传入的email
或mobile
检查现有用户。 如果用户存在,我们将验证给定的Facebook ID与用户记录的Facebook ID匹配。 在这种情况下,我们将创建会话对象。 如果不是,则该方法返回带有403
状态代码的INVALID_CREDENTIALS
错误。
现在开始会话。 请注意,这并不是特别安全。 但是,就本教程而言,它可以正常工作。
对于没有用户与传入email
或mobile
关联的情况,我们要创建一个新记录。 在上面的空if
子句中,添加以下代码:
name = $request->get( 'name' );
$gender = $request->get( 'gender' );
$location = $request->get( 'location' );
if ( empty( $name ) || empty( ( array ) $location ) || empty( $gender ) ) {
$result->error = "ERROR_INVALID_PARAMETERS";
$result->status = 403;
} else {
if ( gettype( $location ) == "string" ) {
$location = json_decode( $location );
}
$locObj = new \stdClass();
$locObj->type = "Point";
$locObj->coordinates = array( $location->lon, $location->lat );
$user->name = $name;
$user->fbId = $fbId;
$user->email = $email;
$user->mobile = $mobile;
$user->gender = $gender;
$user->location = $locObj;
$user = $UserModel->create( $user );
}
我们首先从请求中检索其余的必需参数,然后检查location
参数是作为JSON对象还是编码的JSON对象(字符串)给出。 该方法期望此参数采用以下格式:
{
"lat" : 37.427208696456866,
"lon" : -122.17097282409668
}
然后,我们将此位置转换为MongoDB 2dSphere位置。 要执行地理空间查询,此字段必须具有以下格式:
{
"type" : "Point"
"coordinates" : [ -122.17097282409668, 37.427208696456866 ]
}
我们可以要求客户以这种格式发送位置信息。 但是,最好不要给客户端重新设置用户位置格式的负担,因为这是我们的实现所特有的。
设置位置对象后,我们检查用户所需的参数是否存在,如果是这种情况,我们使用UserModel
类的create
方法创建一个新的用户对象。
而已。 即使我们可以仅通过发送email
和fbId
参数或mobile
和fbId
参数来启动会话,但是如果给出了其余的用户信息,我们的处理程序将在必要时负责创建新用户并启动会话。
7. UserController
类
应用程序需要的最后一个控制器是负责处理Users
资源的控制器。 再一次,我们使用Laravel的工匠命令。 在命令行中,导航到应用程序的根目录并执行以下命令:
php artisan make:controller UserController --plain
这将在app / Http文件夹中的应用程序的Controllers文件夹内创建一个UserController.php文件。 与SessionController
类一样,请确保UserController
类继承自BaseController
并包括UserModel
类。 在类声明内,添加以下代码:
private $_model = null;
public function __construct() {
$this->_model = new UserModel();
}
public function create( Request $request ) {}
public function get( Request $request, $id ) {}
public function remove( Request $request, $id ) {}
public function retrieve( Request $request ) {}
public function update( Request $request, $id ) {}
与SessionController
类一样,我们初始化模型对象并声明Users
资源将支持的方法。 让我们从GET操作开始。 在get
方法中,添加以下代码:
$token = $request->get( 'token' );
$result = new \stdClass();
if ( !$this->_check_session( $token ) ) {
$result->error = "PERMISSION_DENIED";
$result->status = 403;
} else {
$result = $this->_model->get( $id );
}
return $this->_response( $result );
要从系统中检索记录,我们要求用户具有活动会话。 它不必与检索到的用户的ID匹配。 如果用户没有有效的会话,我们将返回PERMISSION_DENIED
错误和403
状态代码。 否则,我们将用户记录作为JSON对象返回。
接下来,对于用户列表,我们需要实现retrieve
方法。 将以下代码添加到retrieve
方法中:
$token = $request->get( 'token' );
$distance = $request->get( 'distance' );
$session = $this->_check_session( $token );
$result = $this->_model->retrieve( ( isset( $session->user_id ) ? $session->user_id : "" ), $distance, $request->get( 'limit' ), $request->get( 'page' ) );
if ( !is_array( $result ) && !$result ) {
$result = new \stdClass();
$result->error = $this->_model->get_error();
$result->status = 403;
}
return $this->_response( $result );
我们首先获取请求参数,尤其是用户的会话令牌和距离参数。 但是,此方法不需要活动的会话。 如果会话有效,则将用户ID传递到UserModel
类的retrieve
方法。
如果传入了distance
参数,则将执行地理空间查询。 如果不是,则执行常规find
查询。 发生错误时,我们会从模型中检索错误,并使用403
状态代码将其返回给用户。 否则,我们将返回一个包含找到的用户的数组。
用户创建将映射到Users
资源的POST操作。 将以下代码添加到create
方法:
$email = $request->get( 'email' );
$fbId = $request->get( 'fbId' );
$gender = $request->get( 'gender' );
$location = $request->get( 'location' );
$mobile = $request->get( 'mobile' );
$name = $request->get( 'name' );
if ( gettype( $location ) == "string" ) {
$location = json_decode( $location );
}
$locObj = new \stdClass();
$locObj->type = "Point";
$locObj->coordinates = array( $location->lon, $location->lat );
$result = new \stdClass();
if ( empty( $name ) || empty( ( array ) $location ) || empty( $fbId ) || empty( $gender ) || ( empty( $email ) && empty( $mobile ) ) ) {
$result->error = "ERROR_INVALID_PARAMETERS";
$result->status = 403;
} else {
$user = array(
"email" => $email,
"fbId" => $fbId,
"gender" => $gender,
"location" => $locObj,
"mobile" => $mobile,
"name" => $name
);
$result = $this->_model->create( $user );
}
return $this->_response( $result );
我们首先为用户检索必要的信息,然后像在会话创建处理程序中一样,将用户的位置转换为适当的格式。
此后,我们检查是否传递了所需的信息。请注意,即使email
和mobile
字段都是可选的,也必须至少显示一个。
这些检查之后,我们调用UserModel
类的create
方法将新用户插入数据库。 最后,我们返回新用户或错误。
要删除用户,我们需要实现remove
方法。 将以下代码添加到remove
方法:
$token = $request->get( 'token' );
$result = new \stdClass();
if ( !$this->_check_session( $token, $id ) ) {
$result->error = "PERMISSION_DENIED";
$result->status = 403;
} else {
$result = $this->_model->remove( $id );
if ( !$result ) {
$result = new \stdClass();
$result->error = $this->_model->get_error();
$result->status = 403;
}
}
return $this->_response( $result );
这是在我们希望这些方法之一_id
要删除的用户的匹配_id
与活动会话的用户。 这是我们验证的第一件事。 在这种情况下,我们委托给模型的remove
方法。 否则,我们将错误设置为PERMISSION_DENIED
并将结果发送回用户。
最后,让我们实现用户的更新操作。 在update
方法内,添加以下代码:
$token = $request->get( 'token' );
$data = new \stdClass();
if ( !empty( $email = $request->get( 'email' ) ) ) {
$data->email = $email;
}
if ( !empty( $fbId = $request->get( 'fbId' ) ) ) {
$data->fbId = $fbId;
}
if ( !empty( $gender = $request->get( 'gender' ) ) ) {
$data->gender = $gender;
}
if ( !empty( $location = $request->get( 'location' ) ) ) {
if ( gettype( $location ) == "string" ) {
$location = json_decode( $location );
}
$locObj = new \stdClass();
$locObj->type = "Point";
$locObj->coordinates = array( $location->lon, $location->lat );
$data->location = $locObj;
}
if ( !empty( $mobile = $request->get( 'mobile' ) ) ) {
$data->mobile = $mobile;
}
if ( !empty( $name = $request->get( 'name' ) ) ) {
$data->name = $name;
}
$result = new \stdClass();
if ( !$this->_check_session( $token, $id ) ) {
$result->error = "PERMISSION_DENIED";
$result->status = 403;
} else {
$result = $this->_model->update( $id, $data );
if ( !$result ) {
$result = new \stdClass();
$result->error = $this->_model->get_error();
$result->status = 403;
}
}
return $this->_response( $result );
我们验证传入的数据并为要更新的内容设置适当的对象。 对于location
参数,我们首先将其重新格式化。
同样,只有活动会话与其自己的_id
相对应的用户才可以访问此方法。 这意味着我们首先检查情况是否如此。
然后,我们调用UserModel
类的update
方法,并将结果返回给客户端。
8.应用路由器
有了最后的代码,我们的API就完成了。 我们有适当的控制器和模型。 我们要做的最后一件事是将传入请求映射到适当的端点。
为此,我们需要编辑应用程序的route.php文件。 它位于app / Http文件夹中。 如果打开它,应该会看到类似以下内容的内容:
Route::get( '/', 'WelcomeController@index' );
当应用程序收到未指定任何资源的GET请求时, WelcomeController
类的index
方法应处理该WelcomeController
。 但是,您可能已经在本教程开始时删除了WelcomeController
。 如果尝试在浏览器中导航到此端点,则会收到错误消息。 让我们用以下代码替换最后一行:
Route::post( 'sessions', 'SessionController@create' );
Route::delete( 'sessions/{token}', 'SessionController@destroy' );
Route::delete( 'users/{id}', 'UserController@remove' );
Route::get( 'users', 'UserController@retrieve' );
Route::get( 'users/{id}', 'UserController@get' );
Route::post( 'users', 'UserController@create' );
Route::put( 'users/{id}', 'UserController@update' );
我们将API请求映射到先前添加到控制器中的方法。 例如,以下调用
[ DELETE ] - http://YOUR_API_URL/sessions/abc
转换为给定URL的DELETE请求。 这意味着在SessionController
delete
方法将以abc
的令牌调用。
结论
使用Laravel 5.0的RESTful API就是这样。 我们支持用户和会话管理,这正是实现iOS客户端所需要的。
在本教程的下一部分中,Jordan将向您展示如何将该API集成到iOS应用程序中。 他还将向您展示如何集成Sinch SDK进行消息传递和语音呼叫。
翻译自: https://code.tutsplus.com/tutorials/creating-a-dating-application-with-sinch-restful-api--cms-23709