Commit bcad0a52 by 孙龙

Merge branch 'feature/sl/20230217' into 'master'

Feature/sl/20230217

See merge request !1
parents 7919aef4 1cbdc065
Showing with 567 additions and 3746 deletions
APP_ENV=production
APP_DEBUG=true
APP_DEBUG=false
DING_ALERT=true
APP_KEY=fasyzIUunp4HcLBudUmkMnDc5H4Z4rNW
......@@ -57,5 +57,5 @@ MAIL_ENCRYPTION=tls
ADMIN=liuzhiyong@teshehui.com
LOGIN_DOMAIN='user.liexin.net'
ERP_DOMAIN=http://192.168.1.235:6886
ERP_DB_NAME=ICHUNT
\ No newline at end of file
ERP_DOMAIN=http://192.168.1.238:6888
ERP_DB_NAME=DEMO
\ No newline at end of file
......@@ -19,3 +19,4 @@ Homestead.json
.DS_Store
*/.DS_Store
.env
/storage/offline_print
......@@ -156,7 +156,7 @@ Class AjaxController extends Controller
$template = TemplateListModel::find($t_id);
//登录
$soap = new \SoapClient(env("ERP_DOMAIN").'/ormrpc/services/EASLogin?wsdl');
$res = $soap->login( 'TC', 'unicom', 'eas', env('ERP_DB_NAME'), 'L2', 1, 'BaseDB');
// $res = $soap->login( 'TC', 'unicom', 'eas', env('ERP_DB_NAME'), 'L2', 1, 'BaseDB');
//接口
$this->erp = new \SoapClient(ENV("ERP_DOMAIN").'/ormrpc/services/WSIchuntjKFacade?wsdl');
$res = $this->erp->createIssueLabel(json_encode([
......@@ -216,7 +216,6 @@ Class AjaxController extends Controller
$data['html'] = $html;
$data['attribute'] = $attribute;
$id = $request->input("t_id",0);
if(intval($id) > 0){
//----------------------------修改模板------------------------------------------------------------------
......
<?php
namespace App\Http\Controllers;
use App\Model\TemplateExtendModel;
use App\Services\LabelService;
use Illuminate\Http\Request;
Class ApiController extends Controller
{
public function getUserLabels(Request $request){
$user_id = $request->input("user_id",0);
$list = LabelService::getUserLabels($user_id);
$list = collect($list)->toArray();
$arr = [];
foreach($list as $k=>$item){
$arr[$k]["template_id"] = $item->id;
$arr[$k]["template_name"] = $item->template_name;
$arr[$k]["html"] = $item->html;
$arr[$k]["attribute"] = $item->attribute;
}
return ['code'=>0, 'count'=>count($arr),'data'=>$arr];
}
/**
* Notes:erp发送数据 准备打印数据
* User: sl
* Date: 2023-02-20 15:35
* @param Request $request
*/
public function tryPrintLabel(Request $request){
$list = $request->input("list",[]);
if(empty($list)){
return $this->setError("请求平台打印失败:打印数据不能为空");
}
foreach($list as &$item){
if(intval($item["print_num"]) <= 0){
$item["print_num"] = 1;
}elseif(intval($item["print_num"]) >= 100){
$item["print_num"] = 100;//单个明细限制100
}
}
if(count($list) > 150){//所有型号数限制150
return $this->setError("请求平台打印失败:型号明细总数超过150了");
}
$templateIdList = array_pluck($list,"template_id");
$templateIdListSearch = LabelService::getTemplateIds($templateIdList);
foreach($templateIdList as $temptId){
if(!in_array($temptId,$templateIdListSearch)){
return $this->setError(sprintf("请求平台打印失败:标签模板id:%s不存在",$temptId));
}
}
$data = json_encode($list);
$key = LabelService::tryPrintLabel($data);
if(empty($key)){
return $this->setError("请求平台打印失败");
}else{
return $this->setSuccessData(
["url" => config("website.erp_print_url")."/api/print_label?key=".(string)$key],0,0,"请求打印成功"
);
}
}
public function printLabel(Request $request){
$key = $request->input("key","");
$html = LabelService::printLabel($key);
$info["html"] = $html;
return view('web.print.prints',$info);
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Http\ApiHelper\ApiCode;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
......@@ -10,4 +11,46 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function setSuccess($msg = '操作成功', $code = 0, $data = [])
{
$res_data = [
"code" => $code,
"msg" => $msg,
'data' => (object)$data,
];
return response()->json($res_data);
}
public function setError($msg, $code = -1, $data = [])
{
$res_data = [
"code" => $code,
"msg" => $msg,
];
if ($data) {
$res_data['data'] = $data;
}
return response()->json($res_data);
}
public function setSuccessData($data = [], $count = 0, $code = 0, $msg = 'ok')
{
$res_data = [
"code" => $code,
"data" => $data,
];
if ($msg) {
$res_data['msg'] = $msg;
}
if ($count) {
$res_data['count'] = $count;
}
return response()->json($res_data);
}
}
......@@ -32,6 +32,7 @@ class Kernel extends HttpKernel
'api' => [
\App\Http\Middleware\JsonpCallback::class,
\App\Http\Middleware\CrossHttp::class,
],
];
......@@ -47,5 +48,7 @@ class Kernel extends HttpKernel
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'cors' => \App\Http\Middleware\CrossHttp::class,
];
}
......@@ -26,7 +26,6 @@ class CheckLogin
if ($pos === 0)
$isApi = true;
$login = Config::get('website.login');
if (!$userId || !$skey || (string)((int)$userId) != $userId || !preg_match('/^[a-zA-Z0-9]+$/', $skey)) {
if ($isApi) {
......@@ -37,7 +36,7 @@ class CheckLogin
}
$cookie = 'oa_user_id=' . $userId . '; oa_skey=' . $skey;
$client = new \GuzzleHttp\Client();
$client = new \GuzzleHttp\Client(["idn_conversion"=>false]);
$rsp = $client->request('GET', $login['check'], [
'headers' => ['Cookie' => $cookie],
'connect_timeout' => 1,
......
<?php
namespace App\Http\Middleware;
use Closure;
class CrossHttp
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$response = $next($request);
$origin = $request->server('HTTP_ORIGIN') ? $request->server('HTTP_ORIGIN') : '';
$allow_origin = config("website.cros");
// if(in_array($origin, $allow_origin)){
$response->header('Access-Control-Allow-Origin',$origin);
$response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Cookie,X-Requested-With, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN');
// $response->header('Access-Control-Expose-Headers', 'Authorization, authenticated');
$response->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS');
$response->header('Access-Control-Allow-Credentials', 'true');
// }
return $response;
}
}
\ No newline at end of file
......@@ -28,6 +28,11 @@ Route::pattern('roleId', '[0-9]+');
|
*/
Route::group(['middleware' => ["cors"]], function () {
Route::match(['get', 'post'],'/api/get_user_labels', 'ApiController@getUserLabels');
Route::match(['get', 'post'],'/api/try_print_label', 'ApiController@tryPrintLabel');
Route::match(['get'],'/api/print_label', 'ApiController@printLabel');
});
Route::group(['middleware' => 'web'], function () {
Route::get('/', 'WebController@index');
......
......@@ -82,5 +82,9 @@ class TemplateListModel extends Model{
}
public static function getTemplatesByIds($ids=[]){
return self::whereIn("id",$ids)->with("template_extend")->get()->toArray();
}
}
\ No newline at end of file
......@@ -12,6 +12,10 @@ use App\Model\NodesModel;
use App\Model\TmplRelationsModel;
use DNS1D;
use DNS2D;
use Illuminate\Support\Facades\Redis;
class LabelService {
public function getLabelHtml($id){
......@@ -96,6 +100,7 @@ class LabelService {
$info["ichunt2020"]["goods_number"] = isset($item["goods_number"]) ? $item["goods_number"] : "";
$info["ichunt2020"]["sku_code"] = isset($item["sku_code"]) ? $item["sku_code"] : "";
$info["ichunt2020"]["customer_code"] = isset($item["customer_code"]) ? $item["customer_code"] : "";
$info["ichunt2020"]["data_code"] = isset($item["data_code"]) ? $item["data_code"] : "";
$info["ichunt2020"]["customer_type"] = isset($item["customer_type"]) ? $item["customer_type"] : "";
$info["ichunt2020"]["customer_com"] = isset($item["customer_com"]) ? $item["customer_com"] : "";
return $this->getHtml($_html,$info);
......@@ -137,6 +142,9 @@ class LabelService {
foreach($yiweimaits as $k=>$yiweimait){
try{
$a = $yiweimait->style;
if(empty($yiweimait->datatypes)){
$yiweimait->datatypes = "暂无数据";
}
$b = 'data:image/png;base64,' . DNS1D::getBarcodePNG($yiweimait->datatypes, "C128");
$c = preg_replace('/background: url\((.*?)\) 0% 0% \/ 100% 100%/'," background: url({$b}) 0% 0% / 100% 100% ",$a);
$yiweimait->style = $c;
......@@ -149,6 +157,9 @@ class LabelService {
foreach($erweimas as $k=>$erweima){
try{
$a =$erweima->style;
if(empty($erweima->datatypes)){
$erweima->datatypes = "暂无数据";
}
$b = 'data:image/png;base64,' . DNS2D::getBarcodePNG($erweima->datatypes, "QRCODE");
$c = preg_replace('/background: url\((.*?)\) 0% 0% \/ 100% 100%/'," background: url({$b}) 0% 0% / 100% 100% ",$a);
$erweima->style = $c;
......@@ -156,7 +167,6 @@ class LabelService {
throw new \Exception(sprintf("生成二维码失败:%s",$e->getMessage()));
}
}
return $dom;
}catch(\Exception $e){
......@@ -164,4 +174,77 @@ class LabelService {
return false;
}
}
public static function getUserLabels($user_id){
$list = \DB::connection("label")->table("template_list")->where("template_list.status",1)
->leftJoin("template_extend","template_list.id","=","template_extend.template_id")
->where(function($q) use($user_id){
$q->where("create_userid",$user_id)->orWhere("create_userid",1000);
})
->select("template_list.id","template_list.template_name","template_extend.html","template_extend.attribute")
->get();
return $list;
}
public static function randStr($len=16){
$base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$count = strlen($base);
$random = '';
for ($i=0; $i < intval($len); $i++) {
$random.=$base[rand(0,$count-1)];
}
return $random;
}
public static function getErpPrintLabelRedisKey(){
for($i=0;;$i++){
$random = self::randStr(30);
$key = md5($random.microtime());
$value = Redis::get($key);
if(empty($value)){
break;
}
sleep(0.1);
}
return $key;
}
public static function tryPrintLabel($data){
$key = self::getErpPrintLabelRedisKey();
Redis::setEx($key,3600*24,$data);
return $key;
}
public static function printLabel($key){
$datas = Redis::get($key);
$datas = json_decode($datas,true);
$template_ids = [];
foreach($datas as $item){
array_push($template_ids,$item["template_id"]);
}
$templateList = TemplateListModel::getTemplatesByIds($template_ids);
$templateList = arrayChangeKeyByField($templateList,"id");
$returnHtml = [];
foreach($datas as $item){
$pringNums = $item["print_num"];
for ($i=0;$i<$pringNums;$i++){
$html = $templateList[$item["template_id"]]["template_extend"]["html"];
$createHtml = (new \App\Services\LabelService)->getLabelOfflineHtml($html,$item);
if($createHtml){
array_push($returnHtml,$createHtml->outertext);
}
}
}
return $returnHtml;
}
public static function getTemplateIds($templateIdList){
$templateListIds = TemplateListModel::whereIn("id",$templateIdList)->where("status",1)->pluck("id")->toArray();
return $templateListIds;
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ class OfflineLabelService{
"sku_code",
"customer_code",
"customer_type",
"data_code",
];
public function getOfflineUploadData($data){
......
<?php
/*
* 遍历数组中某个字段的值 作为 键 返回新数组
*/
function arrayChangeKeyByField($list, $searchKey)
{
$arr = [];
if (!$searchKey) {
return $list;
}
foreach ($list as $k => $v) {
if (isset($v[$searchKey])) {
$arr[$v[$searchKey]] = $v;
}
}
return $arr ? $arr : $list;
}
\ No newline at end of file
......@@ -14,9 +14,10 @@
"redgo/monitor-ding": "0.2",
"hprose/hprose": "^2.0",
"php-amqplib/php-amqplib": "2.7",
"jenssegers/mongodb": "3.2.*",
"milon/barcode": "5.1",
"sunra/php-simple-html-dom-parser": "1.5.2"
"sunra/php-simple-html-dom-parser": "1.5.2",
"predis/predis": "*"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
......@@ -33,7 +34,11 @@
"psr-4": {
"App\\": "app/",
"JsonSchema\\": "vendor/justinrainbow/json-schema/src/"
}
},
"files":[
"app/helps.php"
]
},
"autoload-dev": {
"classmap": [
......
......@@ -159,7 +159,7 @@ return [
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
Redgo\MonitorDing\MonitorDingServiceProvider::class,
Jenssegers\Mongodb\MongodbServiceProvider::class,
// Jenssegers\Mongodb\MongodbServiceProvider::class,
Milon\Barcode\BarcodeServiceProvider::class,
],
......
......@@ -22,4 +22,15 @@ return [
// go 服务
'go_server' => 'http://127.0.0.1:8070/',
"cros" =>[
"http://order.xiaokang.liexinlocal.com",
"http://order.liexindev.net",
"https://order.liexindev.net",
"http://order.ichunt.net",
"https://order.ichunt.net",
],
//erp预打印缓存数据的key
"erp_print_label_redis_key" => "erp_print_label_key",
"erp_print_url" => "http://label.liexindev.net",
];
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
<IfModule deflate_module>
# 对js,html,xml,css,普通文本开启Gzip压缩
AddOutputFilterByType DEFLATE application/x-javascript text/html text/plain text/xml text/css
</IfModule>
......@@ -72,8 +72,9 @@ layui.use(['form', 'table', 'laydate','upload'], function() {
,{field: 'goods_type', title: '型号'}
,{field: 'goods_brand', title: '品牌'}
,{field: 'goods_number', title: '数量',width:80}
,{field: 'customer_type', title: '客户编码'}
,{field: 'customer_code', title: '客户编码'}
,{field: 'customer_type', title: '客户型号'}
,{field: 'data_code', title: '批次'}
]]
,data:data_123456
})
......
......@@ -172,6 +172,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl1 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -241,6 +242,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl1 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -271,6 +273,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl2 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -301,6 +304,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl3 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -331,6 +335,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl4 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......
......@@ -13,6 +13,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl1 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -38,6 +39,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl1 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -68,6 +70,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl2 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -98,6 +101,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl3 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......@@ -128,6 +132,7 @@
<div class="sjglbox fl">
<select class="sjgl sjgl4 fl">
<option value="">自定义</option>
<option value="data_code">批次号</option>
<option value="sku_code">SKU编码</option>
<option value="goods_type">型号名称</option>
<option value="goods_brand">型号品牌</option>
......
<link href="/css/style.css" rel="stylesheet">
<style>
body{background:#fff;margin:0px;}
.print{background:#fff;padding-left:8px; page-break-after: always; }
.edit-con{box-sizing:border-box;top:6px;}
.layui-btn{
display: inline-block;
height: 38px;
line-height: 38px;
padding: 0 18px;
background-color: #009688;
color: #fff;
white-space: nowrap;
text-align: center;
font-size: 14px;
border: none;
border-radius: 2px;
cursor: pointer;
}
</style>
@foreach($html as $item)
<div class="print">
{!! $item !!}
</div>
</div>
@endforeach
<script src="../../../../js/jquery-2.2.1.js"></script>
<script type="text/javascript">
$(function(){
$(".print").width($(".edit-con").width());
if($(".edit-con").height()==280){
$(".print").css({"height":"303px"})
}
$(".huabuitem[type='5'],.huabuitem[type='6'],.huabuitem[type='7']").each(function(){
var urlo_=$(this).css("backgroundImage").replace('url(','').replace(')','');
$(this).css({"background":""})
$(this).html("<img src='"+urlo_.substring(1,urlo_.length-1)+"' />");
if($(this).attr("name")=="yiweimait"){
var max_width=$(this).width();
var width_=$(this).find("img").width();
if(width_>max_width){
width_=max_width
}
$(this).find("img").css({height:"100%",width:width_});
}else{
$(this).find("img").css({width:"100%",height:"100%"})
}
})
setTimeout(function(){
window.print();
}, 100);
})
</script>
......@@ -3,7 +3,7 @@
<style>
body{background:#fff;margin:0px;}
.print{background:#fff;padding-left:8px;}
.print{background:#fff;padding-left:8px; page-break-after: always; }
.edit-con{box-sizing:border-box;top:6px;}
.layui-btn{
......@@ -29,6 +29,7 @@
@endforeach
<script src="../../../../js/jquery-2.2.1.js"></script>
<script type="text/javascript">
$(function(){
$(".print").width($(".edit-con").width());
if($(".edit-con").height()==280){
......
......@@ -37,57 +37,130 @@ namespace Composer\Autoload;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-var array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
......@@ -103,8 +176,10 @@ class ClassLoader
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
......@@ -148,10 +223,12 @@ class ClassLoader
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
......@@ -196,7 +273,9 @@ class ClassLoader
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
......@@ -212,9 +291,11 @@ class ClassLoader
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
......@@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
......@@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
......@@ -276,6 +361,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
......@@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
......@@ -323,6 +429,8 @@ class ClassLoader
return true;
}
return null;
}
/**
......@@ -367,6 +475,21 @@ class ClassLoader
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
......@@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
......
......@@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'CreatePasswordResetsTable' => $baseDir . '/database/migrations/2014_10_12_100000_create_password_resets_table.php',
'CreateUsersTable' => $baseDir . '/database/migrations/2014_10_12_000000_create_users_table.php',
'DatabaseSeeder' => $baseDir . '/database/seeds/DatabaseSeeder.php',
......
......@@ -20,9 +20,9 @@ return array(
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
'58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
'3a37ebac017bc098e9a86b35401e7a68' => $vendorDir . '/mongodb/mongodb/src/functions.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'4a1f389d6ce373bda9e57857d3b61c84' => $vendorDir . '/barryvdh/laravel-debugbar/src/helpers.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'd7f4f7522f962c095f835c50e6136087' => $vendorDir . '/hprose/hprose/src/init.php',
'923ee9fe00f99f09d97aeb64f0339902' => $baseDir . '/app/helps.php',
);
......@@ -12,7 +12,6 @@ return array(
'Mockery' => array($vendorDir . '/mockery/mockery/library'),
'Milon\\Barcode' => array($vendorDir . '/milon/barcode/src'),
'Maatwebsite\\Excel\\' => array($vendorDir . '/maatwebsite/excel/src'),
'Jenssegers\\Mongodb' => array($vendorDir . '/jenssegers/mongodb/src'),
'JakubOnderka\\PhpConsoleHighlighter' => array($vendorDir . '/jakub-onderka/php-console-highlighter/src'),
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib'),
);
......@@ -35,10 +35,10 @@ return array(
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'),
'Predis\\' => array($vendorDir . '/predis/predis/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PhpAmqpLib\\' => array($vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'MongoDB\\' => array($vendorDir . '/mongodb/mongodb/src'),
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src', $vendorDir . '/justinrainbow/json-schema/src/JsonSchema'),
'JakubOnderka\\PhpConsoleColor\\' => array($vendorDir . '/jakub-onderka/php-console-color/src'),
......
......@@ -13,19 +13,24 @@ class ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64::getInitializer($loader));
} else {
......
......@@ -21,11 +21,11 @@ class ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
'f0906e6318348a765ffb6eb24e0d0938' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
'58571171fd5812e6e447dce228f52f4d' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Support/helpers.php',
'3a37ebac017bc098e9a86b35401e7a68' => __DIR__ . '/..' . '/mongodb/mongodb/src/functions.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'4a1f389d6ce373bda9e57857d3b61c84' => __DIR__ . '/..' . '/barryvdh/laravel-debugbar/src/helpers.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'd7f4f7522f962c095f835c50e6136087' => __DIR__ . '/..' . '/hprose/hprose/src/init.php',
'923ee9fe00f99f09d97aeb64f0339902' => __DIR__ . '/../..' . '/app/helps.php',
);
public static $prefixLengthsPsr4 = array (
......@@ -78,13 +78,13 @@ class ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64
'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17,
'Prophecy\\' => 9,
'Predis\\' => 7,
'PhpParser\\' => 10,
'PhpAmqpLib\\' => 11,
),
'M' =>
array (
'Monolog\\' => 8,
'MongoDB\\' => 8,
),
'L' =>
array (
......@@ -253,6 +253,10 @@ class ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64
array (
0 => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy',
),
'Predis\\' =>
array (
0 => __DIR__ . '/..' . '/predis/predis/src',
),
'PhpParser\\' =>
array (
0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser',
......@@ -265,10 +269,6 @@ class ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64
array (
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
),
'MongoDB\\' =>
array (
0 => __DIR__ . '/..' . '/mongodb/mongodb/src',
),
'League\\Flysystem\\' =>
array (
0 => __DIR__ . '/..' . '/league/flysystem/src',
......@@ -379,10 +379,6 @@ class ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64
),
'J' =>
array (
'Jenssegers\\Mongodb' =>
array (
0 => __DIR__ . '/..' . '/jenssegers/mongodb/src',
),
'JakubOnderka\\PhpConsoleHighlighter' =>
array (
0 => __DIR__ . '/..' . '/jakub-onderka/php-console-highlighter/src',
......@@ -398,6 +394,7 @@ class ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'CreatePasswordResetsTable' => __DIR__ . '/../..' . '/database/migrations/2014_10_12_100000_create_password_resets_table.php',
'CreateUsersTable' => __DIR__ . '/../..' . '/database/migrations/2014_10_12_000000_create_users_table.php',
'DatabaseSeeder' => __DIR__ . '/../..' . '/database/seeds/DatabaseSeeder.php',
......
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50600)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}
{
"name": "jenssegers/mongodb",
"description": "A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)",
"keywords": ["laravel","eloquent","mongodb","mongo","database","model","moloquent"],
"homepage": "https://github.com/jenssegers/laravel-mongodb",
"authors": [
{
"name": "Jens Segers",
"homepage": "https://jenssegers.com"
}
],
"license" : "MIT",
"require": {
"illuminate/support": "^5.1",
"illuminate/container": "^5.1",
"illuminate/database": "^5.1",
"illuminate/events": "^5.1",
"mongodb/mongodb": "^1.0.0"
},
"require-dev": {
"phpunit/phpunit": "^5.0|^6.0",
"orchestra/testbench": "^3.1",
"mockery/mockery": "^0.9",
"satooshi/php-coveralls": "^1.0"
},
"autoload": {
"psr-0": {
"Jenssegers\\Mongodb": "src/"
}
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php",
"tests/models",
"tests/seeds"
]
},
"suggest": {
"jenssegers/mongodb-session": "Add MongoDB session support to Laravel-MongoDB",
"jenssegers/mongodb-sentry": "Add Sentry support to Laravel-MongoDB"
},
"extra": {
"laravel": {
"providers": [
"Jenssegers\\Mongodb\\MongodbServiceProvider",
"Jenssegers\\Mongodb\\MongodbQueueServiceProvider"
]
}
}
}
<?php
namespace Jenssegers\Mongodb\Auth;
use DateTime;
use DateTimeZone;
use Illuminate\Auth\Passwords\DatabaseTokenRepository as BaseDatabaseTokenRepository;
use MongoDB\BSON\UTCDateTime;
class DatabaseTokenRepository extends BaseDatabaseTokenRepository
{
/**
* @inheritdoc
*/
protected function getPayload($email, $token)
{
return ['email' => $email, 'token' => $token, 'created_at' => new UTCDateTime(time() * 1000)];
}
/**
* @inheritdoc
*/
protected function tokenExpired($token)
{
// Convert UTCDateTime to a date string.
if ($token['created_at'] instanceof UTCDateTime) {
$date = $token['created_at']->toDateTime();
$date->setTimezone(new DateTimeZone(date_default_timezone_get()));
$token['created_at'] = $date->format('Y-m-d H:i:s');
} elseif (is_array($token['created_at']) and isset($token['created_at']['date'])) {
$date = new DateTime($token['created_at']['date'], new DateTimeZone(isset($token['created_at']['timezone']) ? $token['created_at']['timezone'] : 'UTC'));
$date->setTimezone(new DateTimeZone(date_default_timezone_get()));
$token['created_at'] = $date->format('Y-m-d H:i:s');
}
return parent::tokenExpired($token);
}
}
<?php
namespace Jenssegers\Mongodb\Auth;
use Illuminate\Auth\Passwords\PasswordBrokerManager as BasePasswordBrokerManager;
class PasswordBrokerManager extends BasePasswordBrokerManager
{
/**
* @inheritdoc
*/
protected function createTokenRepository(array $config)
{
$laravel = app();
if (version_compare($laravel::VERSION, '5.4', '>=')) {
return new DatabaseTokenRepository(
$this->app['db']->connection(),
$this->app['hash'],
$config['table'],
$this->app['config']['app.key'],
$config['expire']
);
} else {
return new DatabaseTokenRepository(
$this->app['db']->connection(),
$config['table'],
$this->app['config']['app.key'],
$config['expire']
);
}
}
}
<?php
namespace Jenssegers\Mongodb\Auth;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as BasePasswordResetServiceProvider;
class PasswordResetServiceProvider extends BasePasswordResetServiceProvider
{
/**
* Register the token repository implementation.
*
* @return void
*/
protected function registerTokenRepository()
{
$this->app->singleton('auth.password.tokens', function ($app) {
$connection = $app['db']->connection();
// The database token repository is an implementation of the token repository
// interface, and is responsible for the actual storing of auth tokens and
// their e-mail addresses. We will inject this table and hash key to it.
$table = $app['config']['auth.password.table'];
$key = $app['config']['app.key'];
$expire = $app['config']->get('auth.password.expire', 60);
return new DatabaseTokenRepository($connection, $table, $key, $expire);
});
}
/**
* @inheritdoc
*/
protected function registerPasswordBroker()
{
$this->app->singleton('auth.password', function ($app) {
return new PasswordBrokerManager($app);
});
$this->app->bind('auth.password.broker', function ($app) {
return $app->make('auth.password')->broker();
});
}
}
<?php
namespace Jenssegers\Mongodb\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Jenssegers\Mongodb\Eloquent\Model as Model;
class User extends Model implements
AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
}
<?php
namespace Jenssegers\Mongodb;
use Exception;
use MongoDB\BSON\ObjectID;
use MongoDB\Collection as MongoCollection;
class Collection
{
/**
* The connection instance.
*
* @var Connection
*/
protected $connection;
/**
* The MongoCollection instance..
*
* @var MongoCollection
*/
protected $collection;
/**
* @param Connection $connection
* @param MongoCollection $collection
*/
public function __construct(Connection $connection, MongoCollection $collection)
{
$this->connection = $connection;
$this->collection = $collection;
}
/**
* Handle dynamic method calls.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
$start = microtime(true);
$result = call_user_func_array([$this->collection, $method], $parameters);
if ($this->connection->logging()) {
// Once we have run the query we will calculate the time that it took to run and
// then log the query, bindings, and execution time so we will report them on
// the event that the developer needs them. We'll log time in milliseconds.
$time = $this->connection->getElapsedTime($start);
$query = [];
// Convert the query parameters to a json string.
array_walk_recursive($parameters, function (&$item, $key) {
if ($item instanceof ObjectID) {
$item = (string) $item;
}
});
// Convert the query parameters to a json string.
foreach ($parameters as $parameter) {
try {
$query[] = json_encode($parameter);
} catch (Exception $e) {
$query[] = '{...}';
}
}
$queryString = $this->collection->getCollectionName() . '.' . $method . '(' . implode(',', $query) . ')';
$this->connection->logQuery($queryString, [], $time);
}
return $result;
}
}
<?php
namespace Jenssegers\Mongodb;
use Illuminate\Database\Connection as BaseConnection;
use MongoDB\Client;
class Connection extends BaseConnection
{
/**
* The MongoDB database handler.
*
* @var \MongoDB\Database
*/
protected $db;
/**
* The MongoDB connection handler.
*
* @var \MongoDB\Client
*/
protected $connection;
/**
* Create a new database connection instance.
*
* @param array $config
*/
public function __construct(array $config)
{
$this->config = $config;
// Build the connection string
$dsn = $this->getDsn($config);
// You can pass options directly to the MongoDB constructor
$options = array_get($config, 'options', []);
// Create the connection
$this->connection = $this->createConnection($dsn, $config, $options);
// Select database
$this->db = $this->connection->selectDatabase($config['database']);
$this->useDefaultPostProcessor();
$this->useDefaultSchemaGrammar();
$this->useDefaultQueryGrammar();
}
/**
* Begin a fluent query against a database collection.
*
* @param string $collection
* @return Query\Builder
*/
public function collection($collection)
{
$query = new Query\Builder($this, $this->getPostProcessor());
return $query->from($collection);
}
/**
* Begin a fluent query against a database collection.
*
* @param string $table
* @return Query\Builder
*/
public function table($table)
{
return $this->collection($table);
}
/**
* Get a MongoDB collection.
*
* @param string $name
* @return Collection
*/
public function getCollection($name)
{
return new Collection($this, $this->db->selectCollection($name));
}
/**
* @inheritdoc
*/
public function getSchemaBuilder()
{
return new Schema\Builder($this);
}
/**
* Get the MongoDB database object.
*
* @return \MongoDB\Database
*/
public function getMongoDB()
{
return $this->db;
}
/**
* return MongoDB object.
*
* @return \MongoDB\Client
*/
public function getMongoClient()
{
return $this->connection;
}
/**
* Create a new MongoDB connection.
*
* @param string $dsn
* @param array $config
* @param array $options
* @return \MongoDB\Client
*/
protected function createConnection($dsn, array $config, array $options)
{
// By default driver options is an empty array.
$driverOptions = [];
if (isset($config['driver_options']) && is_array($config['driver_options'])) {
$driverOptions = $config['driver_options'];
}
// Check if the credentials are not already set in the options
if (!isset($options['username']) && !empty($config['username'])) {
$options['username'] = $config['username'];
}
if (!isset($options['password']) && !empty($config['password'])) {
$options['password'] = $config['password'];
}
return new Client($dsn, $options, $driverOptions);
}
/**
* @inheritdoc
*/
public function disconnect()
{
unset($this->connection);
}
/**
* Create a DSN string from a configuration.
*
* @param array $config
* @return string
*/
protected function getDsn(array $config)
{
// Check if the user passed a complete dsn to the configuration.
if (!empty($config['dsn'])) {
return $config['dsn'];
}
// Treat host option as array of hosts
$hosts = is_array($config['host']) ? $config['host'] : [$config['host']];
foreach ($hosts as &$host) {
// Check if we need to add a port to the host
if (strpos($host, ':') === false && !empty($config['port'])) {
$host = $host . ':' . $config['port'];
}
}
// Check if we want to authenticate against a specific database.
$auth_database = isset($config['options']) && !empty($config['options']['database']) ? $config['options']['database'] : null;
return 'mongodb://' . implode(',', $hosts) . ($auth_database ? '/' . $auth_database : '');
}
/**
* @inheritdoc
*/
public function getElapsedTime($start)
{
return parent::getElapsedTime($start);
}
/**
* @inheritdoc
*/
public function getDriverName()
{
return 'mongodb';
}
/**
* @inheritdoc
*/
protected function getDefaultPostProcessor()
{
return new Query\Processor();
}
/**
* @inheritdoc
*/
protected function getDefaultQueryGrammar()
{
return new Query\Grammar();
}
/**
* @inheritdoc
*/
protected function getDefaultSchemaGrammar()
{
return new Schema\Grammar();
}
/**
* Dynamically pass methods to the connection.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return call_user_func_array([$this->db, $method], $parameters);
}
}
<?php
namespace Jenssegers\Mongodb\Eloquent;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Jenssegers\Mongodb\Helpers\QueriesRelationships;
use MongoDB\Driver\Cursor;
use MongoDB\Model\BSONDocument;
class Builder extends EloquentBuilder
{
use QueriesRelationships;
/**
* The methods that should be returned from query builder.
*
* @var array
*/
protected $passthru = [
'toSql',
'insert',
'insertGetId',
'pluck',
'count',
'min',
'max',
'avg',
'sum',
'exists',
'push',
'pull',
];
/**
* @inheritdoc
*/
public function update(array $values, array $options = [])
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation()) {
$relation->performUpdate($this->model, $values);
return 1;
}
return $this->query->update($this->addUpdatedAtColumn($values), $options);
}
/**
* @inheritdoc
*/
public function insert(array $values)
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation()) {
$relation->performInsert($this->model, $values);
return true;
}
return parent::insert($values);
}
/**
* @inheritdoc
*/
public function insertGetId(array $values, $sequence = null)
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation()) {
$relation->performInsert($this->model, $values);
return $this->model->getKey();
}
return parent::insertGetId($values, $sequence);
}
/**
* @inheritdoc
*/
public function delete()
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation()) {
$relation->performDelete($this->model);
return $this->model->getKey();
}
return parent::delete();
}
/**
* @inheritdoc
*/
public function increment($column, $amount = 1, array $extra = [])
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation()) {
$value = $this->model->{$column};
// When doing increment and decrements, Eloquent will automatically
// sync the original attributes. We need to change the attribute
// temporary in order to trigger an update query.
$this->model->{$column} = null;
$this->model->syncOriginalAttribute($column);
$result = $this->model->update([$column => $value]);
return $result;
}
return parent::increment($column, $amount, $extra);
}
/**
* @inheritdoc
*/
public function decrement($column, $amount = 1, array $extra = [])
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation()) {
$value = $this->model->{$column};
// When doing increment and decrements, Eloquent will automatically
// sync the original attributes. We need to change the attribute
// temporary in order to trigger an update query.
$this->model->{$column} = null;
$this->model->syncOriginalAttribute($column);
return $this->model->update([$column => $value]);
}
return parent::decrement($column, $amount, $extra);
}
/**
* @inheritdoc
*/
public function raw($expression = null)
{
// Get raw results from the query builder.
$results = $this->query->raw($expression);
// Convert MongoCursor results to a collection of models.
if ($results instanceof Cursor) {
$results = iterator_to_array($results, false);
return $this->model->hydrate($results);
} // Convert Mongo BSONDocument to a single object.
elseif ($results instanceof BSONDocument) {
$results = $results->getArrayCopy();
return $this->model->newFromBuilder((array) $results);
} // The result is a single object.
elseif (is_array($results) and array_key_exists('_id', $results)) {
return $this->model->newFromBuilder((array) $results);
}
return $results;
}
}
<?php
namespace Jenssegers\Mongodb\Eloquent;
use Jenssegers\Mongodb\Relations\EmbedsMany;
use Jenssegers\Mongodb\Relations\EmbedsOne;
trait EmbedsRelations
{
/**
* Define an embedded one-to-many relationship.
*
* @param string $related
* @param string $localKey
* @param string $foreignKey
* @param string $relation
* @return \Jenssegers\Mongodb\Relations\EmbedsMany
*/
protected function embedsMany($related, $localKey = null, $foreignKey = null, $relation = null)
{
// If no relation name was given, we will use this debug backtrace to extract
// the calling method's name and use that as the relationship name as most
// of the time this will be what we desire to use for the relationships.
if (is_null($relation)) {
list(, $caller) = debug_backtrace(false);
$relation = $caller['function'];
}
if (is_null($localKey)) {
$localKey = $relation;
}
if (is_null($foreignKey)) {
$foreignKey = snake_case(class_basename($this));
}
$query = $this->newQuery();
$instance = new $related;
return new EmbedsMany($query, $this, $instance, $localKey, $foreignKey, $relation);
}
/**
* Define an embedded one-to-many relationship.
*
* @param string $related
* @param string $localKey
* @param string $foreignKey
* @param string $relation
* @return \Jenssegers\Mongodb\Relations\EmbedsOne
*/
protected function embedsOne($related, $localKey = null, $foreignKey = null, $relation = null)
{
// If no relation name was given, we will use this debug backtrace to extract
// the calling method's name and use that as the relationship name as most
// of the time this will be what we desire to use for the relationships.
if (is_null($relation)) {
list(, $caller) = debug_backtrace(false);
$relation = $caller['function'];
}
if (is_null($localKey)) {
$localKey = $relation;
}
if (is_null($foreignKey)) {
$foreignKey = snake_case(class_basename($this));
}
$query = $this->newQuery();
$instance = new $related;
return new EmbedsOne($query, $this, $instance, $localKey, $foreignKey, $relation);
}
}
<?php
namespace Jenssegers\Mongodb\Eloquent;
trait SoftDeletes
{
use \Illuminate\Database\Eloquent\SoftDeletes;
/**
* @inheritdoc
*/
public function getQualifiedDeletedAtColumn()
{
return $this->getDeletedAtColumn();
}
}
<?php
namespace Jenssegers\Mongodb\Helpers;
use Illuminate\Database\Eloquent\Builder;
class EloquentBuilder extends Builder
{
use QueriesRelationships;
}
<?php
namespace Jenssegers\Mongodb\Helpers;
use Closure;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
use Jenssegers\Mongodb\Eloquent\Model;
trait QueriesRelationships
{
/**
* Add a relationship count / exists condition to the query.
*
* @param string $relation
* @param string $operator
* @param int $count
* @param string $boolean
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
{
if (strpos($relation, '.') !== false) {
return $this->hasNested($relation, $operator, $count, $boolean, $callback);
}
$relation = $this->getRelationWithoutConstraints($relation);
// If this is a hybrid relation then we can not use a normal whereExists() query that relies on a subquery
// We need to use a `whereIn` query
if ($this->getModel() instanceof Model || $this->isAcrossConnections($relation)) {
return $this->addHybridHas($relation, $operator, $count, $boolean, $callback);
}
// If we only need to check for the existence of the relation, then we can optimize
// the subquery to only run a "where exists" clause instead of this full "count"
// clause. This will make these queries run much faster compared with a count.
$method = $this->canUseExistsForExistenceCheck($operator, $count)
? 'getRelationExistenceQuery'
: 'getRelationExistenceCountQuery';
$hasQuery = $relation->{$method}(
$relation->getRelated()->newQuery(), $this
);
// Next we will call any given callback as an "anonymous" scope so they can get the
// proper logical grouping of the where clauses if needed by this Eloquent query
// builder. Then, we will be ready to finalize and return this query instance.
if ($callback) {
$hasQuery->callScope($callback);
}
return $this->addHasWhere(
$hasQuery, $relation, $operator, $count, $boolean
);
}
/**
* @param $relation
* @return bool
*/
protected function isAcrossConnections($relation)
{
return $relation->getParent()->getConnectionName() !== $relation->getRelated()->getConnectionName();
}
/**
* Compare across databases
* @param $relation
* @param string $operator
* @param int $count
* @param string $boolean
* @param Closure|null $callback
* @return mixed
* @throws \Exception
*/
public function addHybridHas($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
{
$hasQuery = $relation->getQuery();
if ($callback) {
$hasQuery->callScope($callback);
}
// If the operator is <, <= or !=, we will use whereNotIn.
$not = in_array($operator, ['<', '<=', '!=']);
// If we are comparing to 0, we need an additional $not flip.
if ($count == 0) {
$not = !$not;
}
$relations = $hasQuery->pluck($this->getHasCompareKey($relation));
$relatedIds = $this->getConstrainedRelatedIds($relations, $operator, $count);
return $this->whereIn($this->getRelatedConstraintKey($relation), $relatedIds, $boolean, $not);
}
/**
* Returns key we are constraining this parent model's query with
* @param $relation
* @return string
* @throws \Exception
*/
protected function getRelatedConstraintKey($relation)
{
if ($relation instanceof HasOneOrMany) {
return $this->model->getKeyName();
}
if ($relation instanceof BelongsTo) {
return $relation->getForeignKey();
}
throw new \Exception(class_basename($relation) . ' Is Not supported for hybrid query constraints!');
}
/**
* @param $relation
* @return string
*/
protected function getHasCompareKey($relation)
{
if (method_exists($relation, 'getHasCompareKey')) {
return $relation->getHasCompareKey();
}
return $relation instanceof HasOneOrMany ? $relation->getForeignKeyName() : $relation->getOwnerKey();
}
/**
* @param $relations
* @param $operator
* @param $count
* @return array
*/
protected function getConstrainedRelatedIds($relations, $operator, $count)
{
$relationCount = array_count_values(array_map(function ($id) {
return (string) $id; // Convert Back ObjectIds to Strings
}, is_array($relations) ? $relations : $relations->flatten()->toArray()));
// Remove unwanted related objects based on the operator and count.
$relationCount = array_filter($relationCount, function ($counted) use ($count, $operator) {
// If we are comparing to 0, we always need all results.
if ($count == 0) {
return true;
}
switch ($operator) {
case '>=':
case '<':
return $counted >= $count;
case '>':
case '<=':
return $counted > $count;
case '=':
case '!=':
return $counted == $count;
}
});
// All related ids.
return array_keys($relationCount);
}
}
<?php
namespace Jenssegers\Mongodb;
use Illuminate\Queue\QueueServiceProvider;
use Jenssegers\Mongodb\Queue\Failed\MongoFailedJobProvider;
class MongodbQueueServiceProvider extends QueueServiceProvider
{
/**
* @inheritdoc
*/
protected function registerFailedJobServices()
{
// Add compatible queue failer if mongodb is configured.
if (config('queue.failed.database') == 'mongodb') {
$this->app->singleton('queue.failer', function ($app) {
return new MongoFailedJobProvider($app['db'], config('queue.failed.database'), config('queue.failed.table'));
});
} else {
parent::registerFailedJobServices();
}
}
}
<?php
namespace Jenssegers\Mongodb;
use Illuminate\Support\ServiceProvider;
use Jenssegers\Mongodb\Eloquent\Model;
use Jenssegers\Mongodb\Queue\MongoConnector;
class MongodbServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application events.
*/
public function boot()
{
Model::setConnectionResolver($this->app['db']);
Model::setEventDispatcher($this->app['events']);
}
/**
* Register the service provider.
*/
public function register()
{
// Add database driver.
$this->app->resolving('db', function ($db) {
$db->extend('mongodb', function ($config, $name) {
$config['name'] = $name;
return new Connection($config);
});
});
// Add connector for queue support.
$this->app->resolving('queue', function ($queue) {
$queue->addConnector('mongodb', function () {
return new MongoConnector($this->app['db']);
});
});
}
}
<?php
namespace Jenssegers\Mongodb\Query;
use Illuminate\Database\Query\Grammars\Grammar as BaseGrammar;
class Grammar extends BaseGrammar
{
}
<?php
namespace Jenssegers\Mongodb\Query;
use Illuminate\Database\Query\Processors\Processor as BaseProcessor;
class Processor extends BaseProcessor
{
}
<?php
namespace Jenssegers\Mongodb\Queue\Failed;
use Carbon\Carbon;
use Illuminate\Queue\Failed\DatabaseFailedJobProvider;
class MongoFailedJobProvider extends DatabaseFailedJobProvider
{
/**
* Log a failed job into storage.
*
* @param string $connection
* @param string $queue
* @param string $payload
*
* @return void
*/
public function log($connection, $queue, $payload, $exception)
{
$failed_at = Carbon::now()->getTimestamp();
$this->getTable()->insert(compact('connection', 'queue', 'payload', 'failed_at', 'exception'));
}
/**
* Get a list of all of the failed jobs.
*
* @return array
*/
public function all()
{
$all = $this->getTable()->orderBy('_id', 'desc')->get()->all();
$all = array_map(function ($job) {
$job['id'] = (string) $job['_id'];
return $job;
}, $all);
return $all;
}
/**
* Get a single failed job.
*
* @param mixed $id
* @return array
*/
public function find($id)
{
$job = $this->getTable()->find($id);
$job['id'] = (string) $job['_id'];
return $job;
}
/**
* Delete a single failed job from storage.
*
* @param mixed $id
* @return bool
*/
public function forget($id)
{
return $this->getTable()->where('_id', $id)->delete() > 0;
}
}
<?php
namespace Jenssegers\Mongodb\Queue;
use Illuminate\Database\ConnectionResolverInterface;
use Illuminate\Queue\Connectors\ConnectorInterface;
use Illuminate\Support\Arr;
class MongoConnector implements ConnectorInterface
{
/**
* Database connections.
*
* @var \Illuminate\Database\ConnectionResolverInterface
*/
protected $connections;
/**
* Create a new connector instance.
*
* @param \Illuminate\Database\ConnectionResolverInterface $connections
*/
public function __construct(ConnectionResolverInterface $connections)
{
$this->connections = $connections;
}
/**
* Establish a queue connection.
*
* @param array $config
* @return \Illuminate\Contracts\Queue\Queue
*/
public function connect(array $config)
{
return new MongoQueue(
$this->connections->connection(Arr::get($config, 'connection')),
$config['table'],
$config['queue'],
Arr::get($config, 'expire', 60)
);
}
}
<?php
namespace Jenssegers\Mongodb\Queue;
use Illuminate\Queue\Jobs\DatabaseJob;
class MongoJob extends DatabaseJob
{
/**
* Indicates if the job has been reserved.
*
* @return bool
*/
public function isReserved()
{
return $this->job->reserved;
}
/**
* @return \DateTime
*/
public function reservedAt()
{
return $this->job->reserved_at;
}
}
<?php
namespace Jenssegers\Mongodb\Queue;
use Carbon\Carbon;
use Illuminate\Queue\DatabaseQueue;
use Jenssegers\Mongodb\Connection;
use MongoDB\Operation\FindOneAndUpdate;
class MongoQueue extends DatabaseQueue
{
/**
* The expiration time of a job.
*
* @var int|null
*/
protected $retryAfter = 60;
/**
* The connection name for the queue.
*
* @var string
*/
protected $connectionName;
/**
* @inheritdoc
*/
public function __construct(Connection $database, $table, $default = 'default', $retryAfter = 60)
{
parent::__construct($database, $table, $default, $retryAfter);
$this->retryAfter = $retryAfter;
}
/**
* @inheritdoc
*/
public function pop($queue = null)
{
$queue = $this->getQueue($queue);
if (!is_null($this->retryAfter)) {
$this->releaseJobsThatHaveBeenReservedTooLong($queue);
}
if ($job = $this->getNextAvailableJobAndReserve($queue)) {
return new MongoJob(
$this->container, $this, $job, $this->connectionName, $queue
);
}
}
/**
* Get the next available job for the queue and mark it as reserved.
*
* When using multiple daemon queue listeners to process jobs there
* is a possibility that multiple processes can end up reading the
* same record before one has flagged it as reserved.
*
* This race condition can result in random jobs being run more then
* once. To solve this we use findOneAndUpdate to lock the next jobs
* record while flagging it as reserved at the same time.
*
* @param string|null $queue
*
* @return \StdClass|null
*/
protected function getNextAvailableJobAndReserve($queue)
{
$job = $this->database->getCollection($this->table)->findOneAndUpdate(
[
'queue' => $this->getQueue($queue),
'reserved' => 0,
'available_at' => ['$lte' => Carbon::now()->getTimestamp()],
],
[
'$set' => [
'reserved' => 1,
'reserved_at' => Carbon::now()->getTimestamp(),
],
],
[
'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
'sort' => ['available_at' => 1],
]
);
if ($job) {
$job->id = $job->_id;
}
return $job;
}
/**
* Release the jobs that have been reserved for too long.
*
* @param string $queue
* @return void
*/
protected function releaseJobsThatHaveBeenReservedTooLong($queue)
{
$expiration = Carbon::now()->subSeconds($this->retryAfter)->getTimestamp();
$now = time();
$reserved = $this->database->collection($this->table)
->where('queue', $this->getQueue($queue))
->where(function ($query) use ($expiration, $now) {
// Check for available jobs
$query->where(function ($query) use ($now) {
$query->whereNull('reserved_at');
$query->where('available_at', '<=', $now);
});
// Check for jobs that are reserved but have expired
$query->orWhere('reserved_at', '<=', $expiration);
})->get();
foreach ($reserved as $job) {
$attempts = $job['attempts'] + 1;
$this->releaseJob($job['_id'], $attempts);
}
}
/**
* Release the given job ID from reservation.
*
* @param string $id
* @param int $attempts
* @return void
*/
protected function releaseJob($id, $attempts)
{
$this->database->table($this->table)->where('_id', $id)->update([
'reserved' => 0,
'reserved_at' => null,
'attempts' => $attempts,
]);
}
/**
* @inheritdoc
*/
public function deleteReserved($queue, $id)
{
$this->database->collection($this->table)->where('_id', $id)->delete();
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Builder;
class BelongsTo extends \Illuminate\Database\Eloquent\Relations\BelongsTo
{
/**
* Get the key for comparing against the parent key in "has" query.
*
* @return string
*/
public function getHasCompareKey()
{
return $this->getOwnerKey();
}
/**
* @inheritdoc
*/
public function addConstraints()
{
if (static::$constraints) {
// For belongs to relationships, which are essentially the inverse of has one
// or has many relationships, we need to actually query on the primary key
// of the related models matching on the foreign key that's on a parent.
$this->query->where($this->getOwnerKey(), '=', $this->parent->{$this->foreignKey});
}
}
/**
* @inheritdoc
*/
public function addEagerConstraints(array $models)
{
// We'll grab the primary key name of the related models since it could be set to
// a non-standard name and not "id". We will then construct the constraint for
// our eagerly loading query so it returns the proper models from execution.
$key = $this->getOwnerKey();
$this->query->whereIn($key, $this->getEagerModelKeys($models));
}
/**
* @inheritdoc
*/
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
{
return $query;
}
/**
* Get the owner key with backwards compatible support.
*
* @return string
*/
public function getOwnerKey()
{
return property_exists($this, 'ownerKey') ? $this->ownerKey : $this->otherKey;
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMany;
class BelongsToMany extends EloquentBelongsToMany
{
/**
* Get the key for comparing against the parent key in "has" query.
*
* @return string
*/
public function getHasCompareKey()
{
return $this->getForeignKey();
}
/**
* @inheritdoc
*/
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
{
return $query;
}
/**
* @inheritdoc
*/
protected function hydratePivotRelation(array $models)
{
// Do nothing.
}
/**
* Set the select clause for the relation query.
*
* @param array $columns
* @return array
*/
protected function getSelectColumns(array $columns = ['*'])
{
return $columns;
}
/**
* @inheritdoc
*/
protected function shouldSelect(array $columns = ['*'])
{
return $columns;
}
/**
* @inheritdoc
*/
public function addConstraints()
{
if (static::$constraints) {
$this->setWhere();
}
}
/**
* Set the where clause for the relation query.
*
* @return $this
*/
protected function setWhere()
{
$foreign = $this->getForeignKey();
$this->query->where($foreign, '=', $this->parent->getKey());
return $this;
}
/**
* @inheritdoc
*/
public function save(Model $model, array $joining = [], $touch = true)
{
$model->save(['touch' => false]);
$this->attach($model, $joining, $touch);
return $model;
}
/**
* @inheritdoc
*/
public function create(array $attributes, array $joining = [], $touch = true)
{
$instance = $this->related->newInstance($attributes);
// Once we save the related model, we need to attach it to the base model via
// through intermediate table so we'll use the existing "attach" method to
// accomplish this which will insert the record and any more attributes.
$instance->save(['touch' => false]);
$this->attach($instance, $joining, $touch);
return $instance;
}
/**
* @inheritdoc
*/
public function sync($ids, $detaching = true)
{
$changes = [
'attached' => [],
'detached' => [],
'updated' => [],
];
if ($ids instanceof Collection) {
$ids = $ids->modelKeys();
}
// First we need to attach any of the associated models that are not currently
// in this joining table. We'll spin through the given IDs, checking to see
// if they exist in the array of current ones, and if not we will insert.
$current = $this->parent->{$this->getRelatedKey()} ?: [];
// See issue #256.
if ($current instanceof Collection) {
$current = $ids->modelKeys();
}
$records = $this->formatSyncList($ids);
$detach = array_diff($current, array_keys($records));
// We need to make sure we pass a clean array, so that it is not interpreted
// as an associative array.
$detach = array_values($detach);
// Next, we will take the differences of the currents and given IDs and detach
// all of the entities that exist in the "current" array but are not in the
// the array of the IDs given to the method which will complete the sync.
if ($detaching and count($detach) > 0) {
$this->detach($detach);
$changes['detached'] = (array) array_map(function ($v) {
return is_numeric($v) ? (int) $v : (string) $v;
}, $detach);
}
// Now we are finally ready to attach the new records. Note that we'll disable
// touching until after the entire operation is complete so we don't fire a
// ton of touch operations until we are totally done syncing the records.
$changes = array_merge(
$changes, $this->attachNew($records, $current, false)
);
if (count($changes['attached']) || count($changes['updated'])) {
$this->touchIfTouching();
}
return $changes;
}
/**
* @inheritdoc
*/
public function updateExistingPivot($id, array $attributes, $touch = true)
{
// Do nothing, we have no pivot table.
}
/**
* @inheritdoc
*/
public function attach($id, array $attributes = [], $touch = true)
{
if ($id instanceof Model) {
$model = $id;
$id = $model->getKey();
// Attach the new parent id to the related model.
$model->push($this->foreignKey, $this->parent->getKey(), true);
} else {
if ($id instanceof Collection) {
$id = $id->modelKeys();
}
$query = $this->newRelatedQuery();
$query->whereIn($this->related->getKeyName(), (array) $id);
// Attach the new parent id to the related model.
$query->push($this->foreignKey, $this->parent->getKey(), true);
}
// Attach the new ids to the parent model.
$this->parent->push($this->getRelatedKey(), (array) $id, true);
if ($touch) {
$this->touchIfTouching();
}
}
/**
* @inheritdoc
*/
public function detach($ids = [], $touch = true)
{
if ($ids instanceof Model) {
$ids = (array) $ids->getKey();
}
$query = $this->newRelatedQuery();
// If associated IDs were passed to the method we will only delete those
// associations, otherwise all of the association ties will be broken.
// We'll return the numbers of affected rows when we do the deletes.
$ids = (array) $ids;
// Detach all ids from the parent model.
$this->parent->pull($this->getRelatedKey(), $ids);
// Prepare the query to select all related objects.
if (count($ids) > 0) {
$query->whereIn($this->related->getKeyName(), $ids);
}
// Remove the relation to the parent.
$query->pull($this->foreignKey, $this->parent->getKey());
if ($touch) {
$this->touchIfTouching();
}
return count($ids);
}
/**
* @inheritdoc
*/
protected function buildDictionary(Collection $results)
{
$foreign = $this->foreignKey;
// First we will build a dictionary of child models keyed by the foreign key
// of the relation so that we will easily and quickly match them to their
// parents without having a possibly slow inner loops for every models.
$dictionary = [];
foreach ($results as $result) {
foreach ($result->$foreign as $item) {
$dictionary[$item][] = $result;
}
}
return $dictionary;
}
/**
* @inheritdoc
*/
protected function newPivotQuery()
{
return $this->newRelatedQuery();
}
/**
* Create a new query builder for the related model.
*
* @return \Illuminate\Database\Query\Builder
*/
public function newRelatedQuery()
{
return $this->related->newQuery();
}
/**
* Get the fully qualified foreign key for the relation.
*
* @return string
*/
public function getForeignKey()
{
return $this->foreignKey;
}
/**
* @inheritdoc
*/
public function getQualifiedForeignKeyName()
{
return $this->foreignKey;
}
/**
* Format the sync list so that it is keyed by ID. (Legacy Support)
* The original function has been renamed to formatRecordsList since Laravel 5.3
*
* @deprecated
* @param array $records
* @return array
*/
protected function formatSyncList(array $records)
{
$results = [];
foreach ($records as $id => $attributes) {
if (!is_array($attributes)) {
list($id, $attributes) = [$attributes, []];
}
$results[$id] = $attributes;
}
return $results;
}
/**
* Get the related key with backwards compatible support.
*
* @return string
*/
public function getRelatedKey()
{
return property_exists($this, 'relatedKey') ? $this->relatedKey : $this->otherKey;
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use MongoDB\BSON\ObjectID;
class EmbedsMany extends EmbedsOneOrMany
{
/**
* @inheritdoc
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model) {
$model->setRelation($relation, $this->related->newCollection());
}
return $models;
}
/**
* @inheritdoc
*/
public function getResults()
{
return $this->toCollection($this->getEmbedded());
}
/**
* Save a new model and attach it to the parent model.
*
* @param Model $model
* @return Model|bool
*/
public function performInsert(Model $model)
{
// Generate a new key if needed.
if ($model->getKeyName() == '_id' and !$model->getKey()) {
$model->setAttribute('_id', new ObjectID);
}
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested()) {
$this->associate($model);
return $this->parent->save() ? $model : false;
}
// Push the new model to the database.
$result = $this->getBaseQuery()->push($this->localKey, $model->getAttributes(), true);
// Attach the model to its parent.
if ($result) {
$this->associate($model);
}
return $result ? $model : false;
}
/**
* Save an existing model and attach it to the parent model.
*
* @param Model $model
* @return Model|bool
*/
public function performUpdate(Model $model)
{
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested()) {
$this->associate($model);
return $this->parent->save();
}
// Get the correct foreign key value.
$foreignKey = $this->getForeignKeyValue($model);
// Use array dot notation for better update behavior.
$values = array_dot($model->getDirty(), $this->localKey . '.$.');
// Update document in database.
$result = $this->getBaseQuery()->where($this->localKey . '.' . $model->getKeyName(), $foreignKey)
->update($values);
// Attach the model to its parent.
if ($result) {
$this->associate($model);
}
return $result ? $model : false;
}
/**
* Delete an existing model and detach it from the parent model.
*
* @param Model $model
* @return int
*/
public function performDelete(Model $model)
{
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested()) {
$this->dissociate($model);
return $this->parent->save();
}
// Get the correct foreign key value.
$foreignKey = $this->getForeignKeyValue($model);
$result = $this->getBaseQuery()->pull($this->localKey, [$model->getKeyName() => $foreignKey]);
if ($result) {
$this->dissociate($model);
}
return $result;
}
/**
* Associate the model instance to the given parent, without saving it to the database.
*
* @param Model $model
* @return Model
*/
public function associate(Model $model)
{
if (!$this->contains($model)) {
return $this->associateNew($model);
} else {
return $this->associateExisting($model);
}
}
/**
* Dissociate the model instance from the given parent, without saving it to the database.
*
* @param mixed $ids
* @return int
*/
public function dissociate($ids = [])
{
$ids = $this->getIdsArrayFrom($ids);
$records = $this->getEmbedded();
$primaryKey = $this->related->getKeyName();
// Remove the document from the parent model.
foreach ($records as $i => $record) {
if (in_array($record[$primaryKey], $ids)) {
unset($records[$i]);
}
}
$this->setEmbedded($records);
// We return the total number of deletes for the operation. The developers
// can then check this number as a boolean type value or get this total count
// of records deleted for logging, etc.
return count($ids);
}
/**
* Destroy the embedded models for the given IDs.
*
* @param mixed $ids
* @return int
*/
public function destroy($ids = [])
{
$count = 0;
$ids = $this->getIdsArrayFrom($ids);
// Get all models matching the given ids.
$models = $this->getResults()->only($ids);
// Pull the documents from the database.
foreach ($models as $model) {
if ($model->delete()) {
$count++;
}
}
return $count;
}
/**
* Delete all embedded models.
*
* @return int
*/
public function delete()
{
// Overwrite the local key with an empty array.
$result = $this->query->update([$this->localKey => []]);
if ($result) {
$this->setEmbedded([]);
}
return $result;
}
/**
* Destroy alias.
*
* @param mixed $ids
* @return int
*/
public function detach($ids = [])
{
return $this->destroy($ids);
}
/**
* Save alias.
*
* @param Model $model
* @return Model
*/
public function attach(Model $model)
{
return $this->save($model);
}
/**
* Associate a new model instance to the given parent, without saving it to the database.
*
* @param Model $model
* @return Model
*/
protected function associateNew($model)
{
// Create a new key if needed.
if (!$model->getAttribute('_id')) {
$model->setAttribute('_id', new ObjectID);
}
$records = $this->getEmbedded();
// Add the new model to the embedded documents.
$records[] = $model->getAttributes();
return $this->setEmbedded($records);
}
/**
* Associate an existing model instance to the given parent, without saving it to the database.
*
* @param Model $model
* @return Model
*/
protected function associateExisting($model)
{
// Get existing embedded documents.
$records = $this->getEmbedded();
$primaryKey = $this->related->getKeyName();
$key = $model->getKey();
// Replace the document in the parent model.
foreach ($records as &$record) {
if ($record[$primaryKey] == $key) {
$record = $model->getAttributes();
break;
}
}
return $this->setEmbedded($records);
}
/**
* Get a paginator for the "select" statement.
*
* @param int $perPage
* @return \Illuminate\Pagination\AbstractPaginator
*/
public function paginate($perPage = null)
{
$page = Paginator::resolveCurrentPage();
$perPage = $perPage ?: $this->related->getPerPage();
$results = $this->getEmbedded();
$total = count($results);
$start = ($page - 1) * $perPage;
$sliced = array_slice($results, $start, $perPage);
return new LengthAwarePaginator($sliced, $total, $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
]);
}
/**
* @inheritdoc
*/
protected function getEmbedded()
{
return parent::getEmbedded() ?: [];
}
/**
* @inheritdoc
*/
protected function setEmbedded($models)
{
if (!is_array($models)) {
$models = [$models];
}
return parent::setEmbedded(array_values($models));
}
/**
* @inheritdoc
*/
public function __call($method, $parameters)
{
if (method_exists(Collection::class, $method)) {
return call_user_func_array([$this->getResults(), $method], $parameters);
}
return parent::__call($method, $parameters);
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Model;
use MongoDB\BSON\ObjectID;
class EmbedsOne extends EmbedsOneOrMany
{
/**
* @inheritdoc
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model) {
$model->setRelation($relation, null);
}
return $models;
}
/**
* @inheritdoc
*/
public function getResults()
{
return $this->toModel($this->getEmbedded());
}
/**
* Save a new model and attach it to the parent model.
*
* @param Model $model
* @return Model|bool
*/
public function performInsert(Model $model)
{
// Generate a new key if needed.
if ($model->getKeyName() == '_id' and !$model->getKey()) {
$model->setAttribute('_id', new ObjectID);
}
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested()) {
$this->associate($model);
return $this->parent->save() ? $model : false;
}
$result = $this->getBaseQuery()->update([$this->localKey => $model->getAttributes()]);
// Attach the model to its parent.
if ($result) {
$this->associate($model);
}
return $result ? $model : false;
}
/**
* Save an existing model and attach it to the parent model.
*
* @param Model $model
* @return Model|bool
*/
public function performUpdate(Model $model)
{
if ($this->isNested()) {
$this->associate($model);
return $this->parent->save();
}
// Use array dot notation for better update behavior.
$values = array_dot($model->getDirty(), $this->localKey . '.');
$result = $this->getBaseQuery()->update($values);
// Attach the model to its parent.
if ($result) {
$this->associate($model);
}
return $result ? $model : false;
}
/**
* Delete an existing model and detach it from the parent model.
*
* @return int
*/
public function performDelete()
{
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested()) {
$this->dissociate();
return $this->parent->save();
}
// Overwrite the local key with an empty array.
$result = $this->getBaseQuery()->update([$this->localKey => null]);
// Detach the model from its parent.
if ($result) {
$this->dissociate();
}
return $result;
}
/**
* Attach the model to its parent.
*
* @param Model $model
* @return Model
*/
public function associate(Model $model)
{
return $this->setEmbedded($model->getAttributes());
}
/**
* Detach the model from its parent.
*
* @return Model
*/
public function dissociate()
{
return $this->setEmbedded(null);
}
/**
* Delete all embedded models.
*
* @return int
*/
public function delete()
{
return $this->performDelete();
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\Relation;
use Jenssegers\Mongodb\Eloquent\Model;
abstract class EmbedsOneOrMany extends Relation
{
/**
* The local key of the parent model.
*
* @var string
*/
protected $localKey;
/**
* The foreign key of the parent model.
*
* @var string
*/
protected $foreignKey;
/**
* The "name" of the relationship.
*
* @var string
*/
protected $relation;
/**
* Create a new embeds many relationship instance.
*
* @param Builder $query
* @param Model $parent
* @param Model $related
* @param string $localKey
* @param string $foreignKey
* @param string $relation
*/
public function __construct(Builder $query, Model $parent, Model $related, $localKey, $foreignKey, $relation)
{
$this->query = $query;
$this->parent = $parent;
$this->related = $related;
$this->localKey = $localKey;
$this->foreignKey = $foreignKey;
$this->relation = $relation;
// If this is a nested relation, we need to get the parent query instead.
if ($parentRelation = $this->getParentRelation()) {
$this->query = $parentRelation->getQuery();
}
$this->addConstraints();
}
/**
* @inheritdoc
*/
public function addConstraints()
{
if (static::$constraints) {
$this->query->where($this->getQualifiedParentKeyName(), '=', $this->getParentKey());
}
}
/**
* @inheritdoc
*/
public function addEagerConstraints(array $models)
{
// There are no eager loading constraints.
}
/**
* @inheritdoc
*/
public function match(array $models, Collection $results, $relation)
{
foreach ($models as $model) {
$results = $model->$relation()->getResults();
$model->setParentRelation($this);
$model->setRelation($relation, $results);
}
return $models;
}
/**
* Shorthand to get the results of the relationship.
*
* @return Collection
*/
public function get()
{
return $this->getResults();
}
/**
* Get the number of embedded models.
*
* @return int
*/
public function count()
{
return count($this->getEmbedded());
}
/**
* Attach a model instance to the parent model.
*
* @param Model $model
* @return Model|bool
*/
public function save(Model $model)
{
$model->setParentRelation($this);
return $model->save() ? $model : false;
}
/**
* Attach a collection of models to the parent instance.
*
* @param Collection|array $models
* @return Collection|array
*/
public function saveMany($models)
{
foreach ($models as $model) {
$this->save($model);
}
return $models;
}
/**
* Create a new instance of the related model.
*
* @param array $attributes
* @return Model
*/
public function create(array $attributes = [])
{
// Here we will set the raw attributes to avoid hitting the "fill" method so
// that we do not have to worry about a mass accessor rules blocking sets
// on the models. Otherwise, some of these attributes will not get set.
$instance = $this->related->newInstance($attributes);
$instance->setParentRelation($this);
$instance->save();
return $instance;
}
/**
* Create an array of new instances of the related model.
*
* @param array $records
* @return array
*/
public function createMany(array $records)
{
$instances = [];
foreach ($records as $record) {
$instances[] = $this->create($record);
}
return $instances;
}
/**
* Transform single ID, single Model or array of Models into an array of IDs.
*
* @param mixed $ids
* @return array
*/
protected function getIdsArrayFrom($ids)
{
if ($ids instanceof \Illuminate\Support\Collection) {
$ids = $ids->all();
}
if (!is_array($ids)) {
$ids = [$ids];
}
foreach ($ids as &$id) {
if ($id instanceof Model) {
$id = $id->getKey();
}
}
return $ids;
}
/**
* @inheritdoc
*/
protected function getEmbedded()
{
// Get raw attributes to skip relations and accessors.
$attributes = $this->parent->getAttributes();
// Get embedded models form parent attributes.
$embedded = isset($attributes[$this->localKey]) ? (array) $attributes[$this->localKey] : null;
return $embedded;
}
/**
* @inheritdoc
*/
protected function setEmbedded($records)
{
// Assign models to parent attributes array.
$attributes = $this->parent->getAttributes();
$attributes[$this->localKey] = $records;
// Set raw attributes to skip mutators.
$this->parent->setRawAttributes($attributes);
// Set the relation on the parent.
return $this->parent->setRelation($this->relation, $records === null ? null : $this->getResults());
}
/**
* Get the foreign key value for the relation.
*
* @param mixed $id
* @return mixed
*/
protected function getForeignKeyValue($id)
{
if ($id instanceof Model) {
$id = $id->getKey();
}
// Convert the id to MongoId if necessary.
return $this->getBaseQuery()->convertKey($id);
}
/**
* Convert an array of records to a Collection.
*
* @param array $records
* @return Collection
*/
protected function toCollection(array $records = [])
{
$models = [];
foreach ($records as $attributes) {
$models[] = $this->toModel($attributes);
}
if (count($models) > 0) {
$models = $this->eagerLoadRelations($models);
}
return new Collection($models);
}
/**
* Create a related model instanced.
*
* @param array $attributes
* @return Model
*/
protected function toModel($attributes = [])
{
if (is_null($attributes)) {
return;
}
$model = $this->related->newFromBuilder((array) $attributes);
$model->setParentRelation($this);
$model->setRelation($this->foreignKey, $this->parent);
// If you remove this, you will get segmentation faults!
$model->setHidden(array_merge($model->getHidden(), [$this->foreignKey]));
return $model;
}
/**
* Get the relation instance of the parent.
*
* @return Relation
*/
protected function getParentRelation()
{
return $this->parent->getParentRelation();
}
/**
* @inheritdoc
*/
public function getQuery()
{
// Because we are sharing this relation instance to models, we need
// to make sure we use separate query instances.
return clone $this->query;
}
/**
* @inheritdoc
*/
public function getBaseQuery()
{
// Because we are sharing this relation instance to models, we need
// to make sure we use separate query instances.
return clone $this->query->getQuery();
}
/**
* Check if this relation is nested in another relation.
*
* @return bool
*/
protected function isNested()
{
return $this->getParentRelation() != null;
}
/**
* Get the fully qualified local key name.
*
* @param string $glue
* @return string
*/
protected function getPathHierarchy($glue = '.')
{
if ($parentRelation = $this->getParentRelation()) {
return $parentRelation->getPathHierarchy($glue) . $glue . $this->localKey;
}
return $this->localKey;
}
/**
* @inheritdoc
*/
public function getQualifiedParentKeyName()
{
if ($parentRelation = $this->getParentRelation()) {
return $parentRelation->getPathHierarchy() . '.' . $this->parent->getKeyName();
}
return $this->parent->getKeyName();
}
/**
* Get the primary key value of the parent.
*
* @return string
*/
protected function getParentKey()
{
return $this->parent->getKey();
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany as EloquentHasMany;
class HasMany extends EloquentHasMany
{
/**
* Get the plain foreign key.
*
* @return string
*/
public function getForeignKeyName()
{
return $this->foreignKey;
}
/**
* Get the plain foreign key.
*
* @return string
*/
public function getPlainForeignKey()
{
return $this->getForeignKeyName();
}
/**
* Get the key for comparing against the parent key in "has" query.
*
* @return string
*/
public function getHasCompareKey()
{
return $this->getForeignKeyName();
}
/**
* @inheritdoc
*/
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
{
$foreignKey = $this->getHasCompareKey();
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
}
/**
* Add the constraints for a relationship count query.
*
* @param Builder $query
* @param Builder $parent
* @return Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$foreignKey = $this->getHasCompareKey();
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
}
/**
* Add the constraints for a relationship query.
*
* @param Builder $query
* @param Builder $parent
* @param array|mixed $columns
* @return Builder
*/
public function getRelationQuery(Builder $query, Builder $parent, $columns = ['*'])
{
$query->select($columns);
$key = $this->wrap($this->getQualifiedParentKeyName());
return $query->where($this->getHasCompareKey(), 'exists', true);
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasOne as EloquentHasOne;
class HasOne extends EloquentHasOne
{
/**
* Get the key for comparing against the parent key in "has" query.
*
* @return string
*/
public function getForeignKeyName()
{
return $this->foreignKey;
}
/**
* Get the key for comparing against the parent key in "has" query.
*
* @return string
*/
public function getHasCompareKey()
{
return $this->getForeignKeyName();
}
/**
* Get the plain foreign key.
*
* @return string
*/
public function getPlainForeignKey()
{
return $this->getForeignKeyName();
}
/**
* @inheritdoc
*/
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
{
$foreignKey = $this->getForeignKeyName();
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
}
/**
* Add the constraints for a relationship count query.
*
* @param Builder $query
* @param Builder $parent
* @return Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$foreignKey = $this->getForeignKeyName();
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
}
/**
* Add the constraints for a relationship query.
*
* @param Builder $query
* @param Builder $parent
* @param array|mixed $columns
* @return Builder
*/
public function getRelationQuery(Builder $query, Builder $parent, $columns = ['*'])
{
$query->select($columns);
$key = $this->wrap($this->getQualifiedParentKeyName());
return $query->where($this->getForeignKeyName(), 'exists', true);
}
}
<?php
namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Relations\MorphTo as EloquentMorphTo;
class MorphTo extends EloquentMorphTo
{
/**
* @inheritdoc
*/
public function addConstraints()
{
if (static::$constraints) {
// For belongs to relationships, which are essentially the inverse of has one
// or has many relationships, we need to actually query on the primary key
// of the related models matching on the foreign key that's on a parent.
$this->query->where($this->getOwnerKey(), '=', $this->parent->{$this->foreignKey});
}
}
/**
* @inheritdoc
*/
protected function getResultsByType($type)
{
$instance = $this->createModelByType($type);
$key = $instance->getKeyName();
$query = $instance->newQuery();
return $query->whereIn($key, $this->gatherKeysByType($type))->get();
}
/**
* Get the owner key with backwards compatible support.
*
* @return string
*/
public function getOwnerKey()
{
return property_exists($this, 'ownerKey') ? $this->ownerKey : $this->otherKey;
}
}
<?php
namespace Jenssegers\Mongodb\Schema;
use Illuminate\Database\Connection;
class Blueprint extends \Illuminate\Database\Schema\Blueprint
{
/**
* The MongoConnection object for this blueprint.
*
* @var \Jenssegers\Mongodb\Connection
*/
protected $connection;
/**
* The MongoCollection object for this blueprint.
*
* @var \Jenssegers\Mongodb\Collection|\MongoDB\Collection
*/
protected $collection;
/**
* Fluent columns.
*
* @var array
*/
protected $columns = [];
/**
* @inheritdoc
*/
public function __construct(Connection $connection, $collection)
{
$this->connection = $connection;
$this->collection = $this->connection->getCollection($collection);
}
/**
* @inheritdoc
*/
public function index($columns = null, $name = null, $algorithm = null, $options = [])
{
$columns = $this->fluent($columns);
// Columns are passed as a default array.
if (is_array($columns) && is_int(key($columns))) {
// Transform the columns to the required array format.
$transform = [];
foreach ($columns as $column) {
$transform[$column] = 1;
}
$columns = $transform;
}
if ($name !== null) {
$options['name'] = $name;
}
$this->collection->createIndex($columns, $options);
return $this;
}
/**
* @inheritdoc
*/
public function primary($columns = null, $name = null, $algorithm = null, $options = [])
{
return $this->unique($columns, $name, $algorithm, $options);
}
/**
* @inheritdoc
*/
public function dropIndex($columns = null)
{
$columns = $this->fluent($columns);
// Columns are passed as a default array.
if (is_array($columns) && is_int(key($columns))) {
// Transform the columns to the required array format.
$transform = [];
foreach ($columns as $column) {
$transform[$column] = $column . '_1';
}
$columns = $transform;
}
foreach ($columns as $column) {
$this->collection->dropIndex($column);
}
return $this;
}
/**
* @inheritdoc
*/
public function unique($columns = null, $name = null, $algorithm = null, $options = [])
{
$columns = $this->fluent($columns);
$options['unique'] = true;
$this->index($columns, $name, $algorithm, $options);
return $this;
}
/**
* Specify a non blocking index for the collection.
*
* @param string|array $columns
* @return Blueprint
*/
public function background($columns = null)
{
$columns = $this->fluent($columns);
$this->index($columns, null, null, ['background' => true]);
return $this;
}
/**
* Specify a sparse index for the collection.
*
* @param string|array $columns
* @param array $options
* @return Blueprint
*/
public function sparse($columns = null, $options = [])
{
$columns = $this->fluent($columns);
$options['sparse'] = true;
$this->index($columns, null, null, $options);
return $this;
}
/**
* Specify a geospatial index for the collection.
*
* @param string|array $columns
* @param string $index
* @param array $options
* @return Blueprint
*/
public function geospatial($columns = null, $index = '2d', $options = [])
{
if ($index == '2d' or $index == '2dsphere') {
$columns = $this->fluent($columns);
$columns = array_flip($columns);
foreach ($columns as $column => $value) {
$columns[$column] = $index;
}
$this->index($columns, null, null, $options);
}
return $this;
}
/**
* Specify the number of seconds after wich a document should be considered expired based,
* on the given single-field index containing a date.
*
* @param string|array $columns
* @param int $seconds
* @return Blueprint
*/
public function expire($columns, $seconds)
{
$columns = $this->fluent($columns);
$this->index($columns, null, null, ['expireAfterSeconds' => $seconds]);
return $this;
}
/**
* @inheritdoc
*/
public function create()
{
$collection = $this->collection->getCollectionName();
$db = $this->connection->getMongoDB();
// Ensure the collection is created.
$db->createCollection($collection);
}
/**
* @inheritdoc
*/
public function drop()
{
$this->collection->drop();
}
/**
* @inheritdoc
*/
public function addColumn($type, $name, array $parameters = [])
{
$this->fluent($name);
return $this;
}
/**
* Specify a sparse and unique index for the collection.
*
* @param string|array $columns
* @param array $options
* @return Blueprint
*/
public function sparse_and_unique($columns = null, $options = [])
{
$columns = $this->fluent($columns);
$options['sparse'] = true;
$options['unique'] = true;
$this->index($columns, null, null, $options);
return $this;
}
/**
* Allow fluent columns.
*
* @param string|array $columns
* @return string|array
*/
protected function fluent($columns = null)
{
if (is_null($columns)) {
return $this->columns;
} elseif (is_string($columns)) {
return $this->columns = [$columns];
} else {
return $this->columns = $columns;
}
}
/**
* Allows the use of unsupported schema methods.
*
* @param $method
* @param $args
* @return Blueprint
*/
public function __call($method, $args)
{
// Dummy.
return $this;
}
}
<?php
namespace Jenssegers\Mongodb\Schema;
use Closure;
use Jenssegers\Mongodb\Connection;
class Builder extends \Illuminate\Database\Schema\Builder
{
/**
* @inheritdoc
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* @inheritdoc
*/
public function hasColumn($table, $column)
{
return true;
}
/**
* @inheritdoc
*/
public function hasColumns($table, array $columns)
{
return true;
}
/**
* Determine if the given collection exists.
*
* @param string $collection
* @return bool
*/
public function hasCollection($collection)
{
$db = $this->connection->getMongoDB();
foreach ($db->listCollections() as $collectionFromMongo) {
if ($collectionFromMongo->getName() == $collection) {
return true;
}
}
return false;
}
/**
* @inheritdoc
*/
public function hasTable($collection)
{
return $this->hasCollection($collection);
}
/**
* Modify a collection on the schema.
*
* @param string $collection
* @param Closure $callback
* @return bool
*/
public function collection($collection, Closure $callback)
{
$blueprint = $this->createBlueprint($collection);
if ($callback) {
$callback($blueprint);
}
}
/**
* @inheritdoc
*/
public function table($collection, Closure $callback)
{
return $this->collection($collection, $callback);
}
/**
* @inheritdoc
*/
public function create($collection, Closure $callback = null)
{
$blueprint = $this->createBlueprint($collection);
$blueprint->create();
if ($callback) {
$callback($blueprint);
}
}
/**
* @inheritdoc
*/
public function drop($collection)
{
$blueprint = $this->createBlueprint($collection);
return $blueprint->drop();
}
/**
* @inheritdoc
*/
protected function createBlueprint($collection, Closure $callback = null)
{
return new Blueprint($this->connection, $collection);
}
}
<?php
namespace Jenssegers\Mongodb\Schema;
use Illuminate\Database\Schema\Grammars\Grammar as BaseGrammar;
class Grammar extends BaseGrammar
{
}
<?php
namespace Jenssegers\Mongodb\Validation;
class DatabasePresenceVerifier extends \Illuminate\Validation\DatabasePresenceVerifier
{
/**
* Count the number of objects in a collection having the given value.
*
* @param string $collection
* @param string $column
* @param string $value
* @param int $excludeId
* @param string $idColumn
* @param array $extra
* @return int
*/
public function getCount($collection, $column, $value, $excludeId = null, $idColumn = null, array $extra = [])
{
$query = $this->table($collection)->where($column, 'regex', "/$value/i");
if (!is_null($excludeId) && $excludeId != 'NULL') {
$query->where($idColumn ?: 'id', '<>', $excludeId);
}
foreach ($extra as $key => $extraValue) {
$this->addWhere($query, $key, $extraValue);
}
return $query->count();
}
/**
* Count the number of objects in a collection with the given values.
*
* @param string $collection
* @param string $column
* @param array $values
* @param array $extra
* @return int
*/
public function getMultiCount($collection, $column, array $values, array $extra = [])
{
// Generates a regex like '/(a|b|c)/i' which can query multiple values
$regex = '/(' . implode('|', $values) . ')/i';
$query = $this->table($collection)->where($column, 'regex', $regex);
foreach ($extra as $key => $extraValue) {
$this->addWhere($query, $key, $extraValue);
}
return $query->count();
}
}
<?php
namespace Jenssegers\Mongodb\Validation;
use Illuminate\Validation\ValidationServiceProvider as BaseProvider;
class ValidationServiceProvider extends BaseProvider
{
protected function registerPresenceVerifier()
{
$this->app->singleton('validation.presence', function ($app) {
return new DatabasePresenceVerifier($app['db']);
});
}
}
# Composer
composer.phar
composer.lock
vendor/
# PHPUnit
phpunit.phar
phpunit.xml
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
env:
global:
- KEY_SERVER="hkp://keyserver.ubuntu.com:80"
- MONGO_REPO_URI="http://repo.mongodb.com/apt/ubuntu"
- MONGO_REPO_TYPE="precise/mongodb-enterprise/"
- SOURCES_LOC="/etc/apt/sources.list.d/mongodb.list"
matrix:
- DRIVER_VERSION=1.2.0 SERVER_VERSION=2.6
- DRIVER_VERSION=1.2.0 SERVER_VERSION=3.0
- DRIVER_VERSION=1.2.0 SERVER_VERSION=3.2
matrix:
fast_finish: true
include:
- php: 7.0
env: DRIVER_VERSION=1.2.0 SERVER_VERSION=2.4
- php: 7.0
env: DRIVER_VERSION=devel SERVER_VERSION=3.2
exclude:
- php: 5.4
env: DRIVER_VERSION=stable SERVER_VERSION=2.6
- php: 5.4
env: DRIVER_VERSION=stable SERVER_VERSION=3.0
- php: 5.5
env: DRIVER_VERSION=stable SERVER_VERSION=2.6
- php: 5.5
env: DRIVER_VERSION=stable SERVER_VERSION=3.0
before_install:
- sudo apt-key adv --keyserver ${KEY_SERVER} --recv 7F0CEB10
- sudo apt-key adv --keyserver ${KEY_SERVER} --recv EA312927
- echo "deb ${MONGO_REPO_URI} ${MONGO_REPO_TYPE}${SERVER_VERSION} multiverse" | sudo tee ${SOURCES_LOC}
- sudo apt-get update -qq
install:
- if dpkg --compare-versions ${SERVER_VERSION} le "2.4"; then export SERVER_PACKAGE=mongodb-10gen-enterprise; else export SERVER_PACKAGE=mongodb-enterprise; fi
- sudo apt-get install ${SERVER_PACKAGE}
- sudo apt-get -y install gdb
before_script:
- phpenv config-rm xdebug.ini
- if dpkg --compare-versions ${SERVER_VERSION} le "2.4"; then export SERVER_SERVICE=mongodb; else export SERVER_SERVICE=mongod; fi
- if ! nc -z localhost 27017; then sudo service ${SERVER_SERVICE} start; fi
- mongod --version
- pecl install -f mongodb-${DRIVER_VERSION}
- if [ "$(php -v | grep 'PHP 5.4')" ]; then echo 'extension = mongodb.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
- php --ri mongodb
- composer install --dev --no-interaction --prefer-source
- ulimit -c
- ulimit -c unlimited -S
script:
- ./vendor/bin/phpunit --debug || RESULT=$?
- for i in $(find ./ -maxdepth 1 -name 'core*' -print); do gdb `php -r 'echo PHP_BINARY;'` core* -ex "thread apply all bt" -ex "set pagination 0" -batch; done;
- if [[ ${RESULT} != 0 ]]; then exit $RESULT ; fi;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment