Commit 3977458d by 孙龙

标签

parent 404e6f9f
Showing with 354 additions and 4177 deletions
......@@ -38,12 +38,23 @@ Class ApiController extends Controller
public function tryPrintLabel(Request $request){
$list = $request->input("list",0);
$data = json_encode($list);
$list = LabelService::tryPrintLabel($data);
$key = LabelService::tryPrintLabel($data);
if(empty($key)){
return ['code'=>-1,"msg"=>"请求平台打印失败", 'data'=>[]];
}else{
return ['code'=>0,"msg"=>"请求平台打印成功", 'data'=>[
"url" => config("website.erp_print_url")."/api/print_label?key=".(string)$key
]];
}
}
public function printLabel(Request $request){
$key = $request->input("key","");
$html = LabelService::printLabel($key);
$info["html"] = $html;
return view('web.print.prints',$info);
}
......
......@@ -16,13 +16,13 @@ class CrossHttp
$response = $next($request);
$origin = $request->server('HTTP_ORIGIN') ? $request->server('HTTP_ORIGIN') : '';
$allow_origin = config("website.cros");
if(in_array($origin, $allow_origin)){
// 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;
}
......
......@@ -31,7 +31,7 @@ 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', 'post'],'/api/print_label', 'ApiController@printLabel');
Route::match(['get'],'/api/print_label', 'ApiController@printLabel');
});
Route::group(['middleware' => 'web'], function () {
......
......@@ -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){
......@@ -156,7 +160,6 @@ class LabelService {
throw new \Exception(sprintf("生成二维码失败:%s",$e->getMessage()));
}
}
return $dom;
}catch(\Exception $e){
......@@ -177,7 +180,58 @@ class LabelService {
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::set($key,$data);
return $key;
}
public static function printLabel($key){
$datas = Redis::get($key);
$datas = json_decode($datas,true);
$template_ids = [];
foreach($datas as $item){
if(intval($item["template_id"]) <= 0){
throw new \Exception("没有找到对应的标签模板id");
}
array_push($template_ids,$item["template_id"]);
}
$templateList = TemplateListModel::getTemplatesByIds($template_ids);
$templateList = arrayChangeKeyByField($templateList,"id");
$returnHtml = [];
foreach($datas as $item){
$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;
}
}
\ No newline at end of file
<?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,
],
......
......@@ -29,5 +29,8 @@ return [
"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",
];
<link href="/css/style.css" rel="stylesheet">
<style>
body{background:#fff;margin:0px;}
.print{background:#fff;padding-left:8px;}
.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>
......@@ -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)
{
......@@ -102,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* 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 bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @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)
{
......@@ -147,11 +222,13 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* 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 bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @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)
{
......@@ -195,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
......@@ -211,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* 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 $prefix The prefix/namespace, with trailing '\\'
* @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',
......
This diff could not be displayed because it is too large.
<?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;
# Contributing to the PHP Library for MongoDB
## Initializing the Repository
Developers who would like to contribute to the library will need to clone it and
initialize the project dependencies with [Composer](https://getcomposer.org/):
```
$ git clone https://github.com/mongodb/mongo-php-library.git
$ cd mongo-php-library
$ composer update
```
In addition to installing project dependencies, Composer will check that the
required extension version is installed. Directions for installing the extension
may be found [here](http://php.net/manual/en/mongodb.installation.php).
Installation directions for Composer may be found in its
[Getting Started](https://getcomposer.org/doc/00-intro.md) guide.
## Testing
The library's test suite uses [PHPUnit](https://phpunit.de/), which should be
installed as a development dependency by Composer.
The test suite may be executed with:
```
$ vendor/bin/phpunit
```
The `phpunit.xml.dist` file is used as the default configuration file for the
test suite. In addition to various PHPUnit options, it defines required
`MONGODB_URI` and `MONGODB_DATABASE` environment variables. You may customize
this configuration by creating your own `phpunit.xml` file based on the
`phpunit.xml.dist` file we provide.
### Testing on HHVM
By default, the PHPUnit script relies on the `php` interpreter for your shell
(i.e. `#!/usr/bin/env php`). You can run the test suite with HHVM like so:
```
$ hhvm vendor/bin/phpunit
```
## Documentation
Documentation for the library lives in the `docs/` directory and is built with
tools in the related
[mongodb/docs-php-library](https://github.com/mongodb/docs-php-library)
repository. The tools repository is already configured to reference our sources.
That said, any changes to the documentation should be tested locally before
committing. Follow the following steps to build the docs locally with the tools
repository:
* Clone the
[mongodb/docs-php-library](https://github.com/mongodb/docs-php-library) tools
repository.
* Install [giza](https://pypi.python.org/pypi/giza/), as noted in the tools
README.
* Sync your working copy of the documentation to the `source/` directory with
`rsync -a --delete /path/to/mongo-php-library/docs/ source/`.
* Build the documentation with `giza make publish`. You can suppress
informational log messages with the `--level warning` option.
* Generated documentation may be found in the `build/master/html` directory.
## Releasing
The follow steps outline the release process for a maintenance branch (e.g.
releasing the `vX.Y` branch as X.Y.Z).
### Ensure PHP version compatibility
Ensure that the library test suite completes on supported versions of PHP and
HHVM.
### Transition JIRA issues and version
All issues associated with the release version should be in the "Closed" state
and have a resolution of "Fixed". Issues with other resolutions (e.g.
"Duplicate", "Works as Designed") should be removed from the release version so
that they do not appear in the release notes.
Check the corresponding ".x" fix version to see if it contains any issues that
are resolved as "Fixed" and should be included in this release version.
Update the version's release date and status from the
[Manage Versions](https://jira.mongodb.org/plugins/servlet/project-config/PHPLIB/versions)
page.
### Update version info
The PHP library uses [semantic versioning](http://semver.org/). Do not break
backwards compatibility in a non-major release or your users will kill you.
Before proceeding, ensure that the `master` branch is up-to-date with all code
changes in this maintenance branch. This is important because we will later
merge the ensuing release commits up to master with `--strategy=ours`, which
will ignore changes from the merged commits.
A version constant may be added at a later date (see:
[PHPLIB-131](https://jira.mongodb.org/browse/PHPLIB-131)). For now, there is
nothing to update.
### Tag release
The maintenance branch's HEAD will be the target for our release tag:
```
$ git tag -a -m "Release X.Y.Z" X.Y.Z
```
### Push tags
```
$ git push --tags
```
### Merge the maintenance branch up to master
```
$ git checkout master
$ git merge vX.Y --strategy=ours
$ git push
```
The `--strategy=ours` option ensures that all changes from the merged commits
will be ignored.
### Publish release notes
The following template should be used for creating GitHub release notes via
[this form](https://github.com/mongodb/mongo-php-library/releases/new).
```
The PHP team is happy to announce that version X.Y.Z of our MongoDB PHP library is now available. This library is a high-level abstraction for the PHP 5, PHP 7, and HHVM drivers (i.e. [`mongodb`](http://php.net/mongodb) extension).
**Release Highlights**
<one or more paragraphs describing important changes in this release>
A complete list of resolved issues in this release may be found at:
$JIRA_URL
**Documentation**
Documentation for this library may be found at:
https://docs.mongodb.com/php-library/
**Feedback**
If you encounter any bugs or issues with this library, please report them via this form:
https://jira.mongodb.org/secure/CreateIssue.jspa?pid=12483&issuetype=1
**Installation**
This library may be installed or upgraded with:
composer require mongodb/mongodb
Installation instructions for the PHP and HHVM driver may be found in the [PHP.net documentation](http://php.net/manual/en/mongodb.installation.php).
```
The URL for the list of resolved JIRA issues will need to be updated with each
release. You may obtain the list from
[this form](https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=12483).
If commits from community contributors were included in this release, append the
following section:
```
**Thanks**
Thanks for our community contributors for this release:
* [$CONTRIBUTOR_NAME](https://github.com/$GITHUB_USERNAME)
```
Release announcements should also be sent to the `mongodb-user@googlegroups.com`
and `mongodb-announce@googlegroups.com` mailing lists.
Consider announcing each release on Twitter. Significant releases should also be
announced via [@MongoDB](http://twitter.com/mongodb) as well.
.PHONY: composer test
COMPOSER_ARGS=update --no-interaction --prefer-source
composer:
@command -v composer >/dev/null 2>&1; \
if test $$? -eq 0; then \
composer $(COMPOSER_ARGS); \
elif test -r composer.phar; then \
php composer.phar $(COMPOSER_ARGS); \
else \
echo >&2 "Cannot find composer; aborting."; \
false; \
fi
test: composer
vendor/bin/phpunit
MongoDB library for PHP
=======================
This library provides a high-level abstraction around the lower-level drivers for
[PHP](https://github.com/mongodb/mongo-php-driver) and
[HHVM](https://github.com/mongodb/mongo-hhvm-driver) (i.e. the `mongodb`
extension).
While the extension provides a limited API for executing commands, queries, and
write operations, this library implements an API similar to that of the
[legacy PHP driver](http://php.net/manual/en/book.mongo.php). It contains
abstractions for client, database, and collection objects, and provides methods
for CRUD operations and common commands (e.g. index and collection management).
If you are developing an application with MongoDB, you should consider using
this library, or another high-level abstraction, instead of the extension alone.
For further information about the architecture of this library and the `mongodb`
extension, see:
- http://www.mongodb.com/blog/post/call-feedback-new-php-and-hhvm-drivers
## Documentation
- https://docs.mongodb.com/php-library/
# Installation
As a high-level abstraction for the driver, this library naturally requires that
the [`mongodb` extension be installed](http://php.net/manual/en/mongodb.installation.php):
$ pecl install mongodb
$ echo "extension=mongodb.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
The preferred method of installing this library is with
[Composer](https://getcomposer.org/) by running the following from your project
root:
$ composer require mongodb/mongodb
## Reporting Issues
Please use the following form to report any issues:
- https://jira.mongodb.org/secure/CreateIssue.jspa?pid=12483&issuetype=1
{
"name": "mongodb/mongodb",
"description": "MongoDB driver library",
"keywords": ["database", "driver", "mongodb", "persistence"],
"homepage": "https://jira.mongodb.org/browse/PHPLIB",
"license": "Apache-2.0",
"authors": [
{ "name": "Hannes Magnusson", "email": "bjori@mongodb.com" },
{ "name": "Jeremy Mikola", "email": "jmikola@gmail.com" },
{ "name": "Derick Rethans", "email": "github@derickrethans.nl" }
],
"require": {
"php": ">=5.4",
"ext-mongodb": "^1.2.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8"
},
"autoload": {
"psr-4": { "MongoDB\\": "src/" },
"files": [ "src/functions.php" ]
},
"autoload-dev": {
"psr-4": { "MongoDB\\Tests\\": "tests/" }
}
}
arg_name: option
name: typeMap
type: array
description: |
Default :php:`type map
<manual/en/mongodb.persistence.deserialization.php#mongodb.persistence.typemaps>`
to apply to cursors, which determines how BSON documents are converted to PHP
values. The |php-library| uses the following type map by default:
.. code-block:: php
[
'array' => 'MongoDB\Model\BSONArray',
'document' => 'MongoDB\Model\BSONDocument',
'root' => 'MongoDB\Model\BSONDocument',
]
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: allow_invalid_hostname
type: boolean
description: |
Disables hostname validation if ``true``. Defaults to ``false``.
Allowing invalid hostnames may expose the driver to a `man-in-the-middle
attack <https://en.wikipedia.org/wiki/Man-in-the-middle_attack>`_.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: ca_dir
type: string
description: |
Path to a correctly hashed certificate directory. The system certificate store
will be used by default.
Falls back to the deprecated ``capath`` SSL context option if not specified.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: ca_file
type: string
description: |
Path to a certificate authority file. The system certificate store will be
used by default.
Falls back to the deprecated ``cafile`` SSL context option if not specified.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: crl_file
type: string
description: |
Path to a certificate revocation list file.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: pem_file
type: string
description: |
Path to a PEM encoded certificate to use for client authentication.
Falls back to the deprecated ``local_cert`` SSL context option if not
specified.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: pem_pwd
type: string
description: |
Passphrase for the PEM encoded certificate (if applicable).
Falls back to the deprecated ``passphrase`` SSL context option if not
specified.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: weak_cert_validation
type: boolean
description: |
Disables certificate validation ``true``. Defaults to ``false``.
Falls back to the deprecated ``allow_self_signed`` SSL context option if not
specified.
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: context
type: resource
description: |
:php:`SSL context options <manual/en/context.ssl.php>` to be used as fallbacks
for other driver options (as specified). Note that the driver does not consult
the default stream context.
This option is supported for backwards compatibility, but should be considered
deprecated.
interface: phpmethod
operation: ~
optional: true
...
arg_name: param
name: $uri
type: string
description: |
The URI of the standalone, replica set, or sharded cluster to which to
connect. Refer to :manual:`Connection String URI Format
</reference/connection-string>` in the MongoDB manual for more information.
Defaults to ``"mongodb://127.0.0.1:27017"`` if unspecified.
Any special characters in the URI components need to be encoded according to
`RFC 3986 <http://www.faqs.org/rfcs/rfc3986.html>`_. This is particularly
relevant to the username and password, which can often include special
characters such as ``@``, ``:``, or ``%``. When connecting via a Unix domain
socket, the socket path may contain special characters such as slashes and
must be encoded. The :php:`rawurlencode() <rawurlencode>` function may be used
to encode constituent parts of the URI.
interface: phpmethod
operation: ~
optional: true
---
arg_name: param
name: $uriOptions
type: array
description: |
Specifies additional URI options, such as authentication credentials or query
string parameters. The options specified in ``$uriOptions`` take precedence
over any analogous options present in the ``$uri`` string and do not need to
be encoded according to `RFC 3986 <http://www.faqs.org/rfcs/rfc3986.html>`_.
Refer to the :php:`MongoDB\\Driver\\Manager::__construct()
<mongodb-driver-manager.construct>` extension reference and :manual:`MongoDB
connection string </reference/connection-string>` documentation for accepted
options.
interface: phpmethod
operation: ~
optional: true
---
arg_name: param
name: $driverOptions
type: array
description: |
Specify driver-specific options, such as SSL options. In addition to any
options supported by the :php:`extension <mongodb-driver-manager>`, the
|php-library| allows you to specify a default :php:`type map
<manual/en/mongodb.persistence.deserialization.php#mongodb.persistence.typemaps>`
to apply to the cursors it creates.
interface: phpmethod
operation: ~
optional: true
...
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