moloquent / moloquent

A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)
https://moloquent.github.io
122 stars 36 forks source link

[bug] with dates #38

Closed ctf0 closed 7 years ago

ctf0 commented 7 years ago

the fields are saved as

  "created_at": new Date("2016-08-09T21:47:12+0200"),
  "updated_at": new Date("2016-09-30T14:36:55+0200")

doing something like dd(Article::first()->created_at); return

Carbon {#216 ▼
  +"date": "2016-08-09 19:55:04.000000"
  +"timezone_type": 3
  +"timezone": "UTC"
}

setting the attr like

public function getCreatedAtAttribute($value)
    {
        return Carbon::parse($value)->toDateTimeString();
    }

return

DateTime::__construct(): Failed to parse time string (1470772504000) at position 12 (0): Unexpected character

using the hack from https://github.com/jenssegers/date/issues/225 as "@{$value}" return "48576-12-03 21:46:40"

AmmarRahman commented 7 years ago

dates are stored as Mongodb UTCDateTime at the database. When you request created_at attribute, Moloquent automatically converts that to a Carbon instance. However, you are overriding the default date mutator, hence $value is an instance of MongoDB\BSON\UTCDateTime not Carbon. If you want, you can use $value->toDateTime() which would return a PhP DateTime object.

ctf0 commented 7 years ago

dd(Carbon::parse($value->toDateTime())->toDateTimeString());

Type error: DateTime::__construct() expects parameter 1 to be string, object given
AmmarRahman commented 7 years ago

to DateTime returns a native php DateTime object. If you need to use Carbon you can write:

Carbon::instance($value->toDateTime())->toDateTimeString();

However, you do not need to convert it to Carbon, You can just use the native php method;

$value->toDateTime()->format('Y-m-d H:i:s');

http://php.net/manual/en/datetime.format.php

ctf0 commented 7 years ago

@fuck yeaaaah 💃 , many thanx btw if its not to much to ask, am currently resolving the ManyToMany relation ids as objectId but on the other side of the relation Moloquent saves the ids as string, is there anyway to check the type of field on the one side and save it the same on the other ?

Model\Post

public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }

Model\Tag

 public function posts()
    {
        return $this->belongsToMany(Post::class);
    }

on the posts collection

"tag_ids": [
    ObjectId("580e15ef9a892011142f1efc")
  ]

on the tags collection

"article_ids": [
    "580e15ed9a892011142f1b87",
  ]
AmmarRahman commented 7 years ago

you can always use php's function

is_a($value, "ObjectId");

However, I have a feeling that you are overcomplicating something here!! http://php.net/manual/en/function.is-a.php

ctf0 commented 7 years ago

no , am just trying to make the usage over mongo as similar to sql as possible,

if you have time check https://github.com/ctf0/MysqlToMongoDb i found away to update the field types b4 saving it to mongo as currently mongo doesnt support this even with $rename which change the field name rather than its type ,

however from what i read that the objectId type is smaller in size than the string type, again as you are much more experienced in this than me so i thought i would ask u for guidance 😊

AmmarRahman commented 7 years ago

I'm not sure why would someone want Mongo to be similar to sql. The whole idea of NoSQL databases is that they are not similar to SQL. biggest advantage is embedding documents, which would be very difficult to automate for conversion.

Anyway, The idea of migrating a database by passing it through an ORM might not be the most efficient way to go through this since you you need to run a lot of code at high level for every entry. You probably noticed that you are having to resort to raw mongo driver commands anyway in many cases. So, if you decouple it from laravel, it would be faster and more useful to a wider audience. I haven't tried migrating a database from MySQL to mongo, but I've heard of some software called Mogofy that does it. you might want to peek at it.

With mongo you define the field type by inserting a field of the required type in the database there is no need to change type.

With ObjectId, my understanding is that it provides better performance on lower level because it is a hash. with a hash, the cpu can run a comparison in one cycle, while comparing a string would consume a cpu cycle per character.

ctf0 commented 7 years ago

similar means that on the high level u still do everything the same without caring what the under layer is , for example. driving a car, u still drive every car the same no matter if its a 1910 car or a 2025 hybrid.

i know about http://mongify.com/ but my experience with it didnt go as smooth as the site demo shows, also the reason i wanted to migrate from mysql to mongo is to ease the learning curve & so i can get as close as possible to a live copy of the code which uses mysql. that's why i built the package over laravel and not from scratch with plain php or even through the mongo shell.

am planning on using luman instead and wrap all of that in a one tiny app with node, so everyone can use it but not yet sure if this would work or not as i've never done that b4.

anyhow regarding the objectId , i've undone this part in the package so it follows the same pattern as moloquent but if you decided later to use it instead of string, i believe it would be better especially with large collections which using the objectId would make much more sense.

AmmarRahman commented 7 years ago

Since you do not have much data to move and you are happy to do it on high level, why don't you go all high. Stop worrying about the operation of the database and let Eloquent/Moloquent deal with it.

switch your BaseModel to Moloquent and temporary set on it leave your default connection to mysql

class BaseModel extends Moloquent {
   protected $primaryKey = 'id';
}

then your script can be as simple as

$models =['User','Post','Comment']
foreach($models as $model){
   foreach($model::all() as $item){
      $data = $item->getArray();
      $newItem = new $model($data);
      $newItem->old_id = $item->id;
      $newItem->setKeyName('_id');
      $newItem->setConnection('mongo');
      $newItem->save();
   }
}

don't forget to remove the setting of the primary key from your BaseModel before you switch your default connection.

ctf0 commented 7 years ago

WOOOOT :scream: , let me try that and get back to u

ctf0 commented 7 years ago

@iceheat

$models =['App\Http\Models\Role', 'App\Http\Models\Article', 'App\User'];
foreach($models as $model){
   foreach($model::all() as $item){
      $data = $item->getArray();
      $newItem = new $model($data);
      $newItem->old_id = $item->id;
      $newItem->setKeyName('_id');
      $newItem->setConnection('mongo');
      $newItem->save();
   }
}
Type error: Argument 1 passed to Moloquent\Query\Builder::__construct() must be an instance of Moloquent\Connection, 
instance of Illuminate\Database\MySqlConnection given, 
called in /home/vagrant/www/vendor/moloquent/moloquent/src/Eloquent/Model.php on line 549
AmmarRahman commented 7 years ago

Did you change your models to use moloquent first?

ctf0 commented 7 years ago

yeap :)

Xplouder commented 7 years ago

@ctf0 did you solve your last error "Argument 1 passed to Moloquent\Query..." ? i'm facing the same... :|

ctf0 commented 7 years ago

@Xplouder can u make a new ticket with the code used ?

Xplouder commented 7 years ago

@ctf0 Done. Check #40