How do you write model logic with NDB & NDBT (in Nette 2.3)

4 years ago

juzna.cz
Member | 249
+
0
-

TL;DR: what's the best practice for handling model logic in NDB & NDBT, which would allow me:

Longer version:
Hi, I've been traditionally working with Doctrine 2, where you define your model classes explicitly and then it's very easy to manipulate your rows / entities in your complex business model logic, and eventually save the changes, e.g.:

<?php
class MySuperService {
  // Approve objection to a change request, and update the change request accordingly.
  public function approveObjection(ChangeRequest $request, Objection $objection) {
    if ($objection->isOpen()) {
       $objection->status = '...';
    }
    if (...) {
       $this->updateRequestDeadline($request);  // possibly modifies $request->deadline and other fields
    }
    $this->updateWeight($request);  // more logic which updates $request
    $this->em->flush();  // save changes to db
  }
?>

On a smaller projects, I decided to use Nette Database (NDB) & Nette Database Table (NDBT), because it's simpler and you don't need to write so much code. It was in version 2.1, and my business logic looked very similar. Concrete model classes were replaced by generic ActiveRow:

<?php
class MySuperService {
  // Approve objection to a change request, and update the change request accordingly.
  public function approveObjection(ActiveRow $request, ActiveRow $objection) {
    if ($objection->status === 'open') {
       $objection->status = '...';  // works in Nette 2.1
    }
    if (...) {
       $this->updateRequestDeadline($request);  // possibly modifies $request->deadline and other fields
    }
    $this->updateWeight($request);  // more logic which updates $request

    // save changes to db
    $request->update();  // works in Nette 2.1
    $objection->update();
  }
?>

NDB and especially NDBT made it really easy to fetch data from database and pass it to templates. And it also allowed me to have complex logic in the business model.

However, when I tried upgrading to Nette 2.3, I noticed that this functionality has been removed.

That means I'd have to convert ActiveRow to an array or to my custom model class, process the business logic, and then save it back to the database. This seems very weird and schizophrenic to me – some code working with ActiveRow and some with different representation.

How do you handle model classes with NDB & NDBT? Do you sometimes use ActiveRow and sometimes another representation? Do you always convert to your representation? What's the best approach?

4 years ago

hrach
Member | 1817
+
0
-

@juzna.cz Well, what a nice question. Nette Database does not allow and support such modeling as it is possible with ORMs. Returned rows are just simple crates. Direct writing access in NDB < 2.3 was causing problems, therefore it was changed. To use advanced modeling over NDB, it is needed to use some advanced|other tools. I didn't agree with David's point of view – such as no possibility to set own row class, that's reason I developed own orm.

4 years ago

juzna.cz
Member | 249
+
0
-

That's not really advanced modeling, just working with the simple crates.

You can do the same with plain old PHP:

<?php
$request = mysql_fetch_object(mysql_query("SELECT * FROM ... WHERE ID=1"));
$request->deadline += 10;
$this->updateWeight($request); // changes $request->weight

$fields = [];
foreach ($request as $k=>$v) $fields[] = "`$k`='$v'"; // this would need better escaping in reality
mysql_query("UPDATE ... SET " . join(',', $fields) . " WHERE ID=$request->ID");
?>

You can use the same with PDO, Dibi or basically any other database layer.

4 years ago

Jan Tvrdík
Nette guru | 2559
+
0
-

BTW: This change is the reason why I decided not to use NDB on one project. The only workaround I know is to add some abstraction which kills the NDB simplicity.

4 years ago

juzna.cz
Member | 249
+
+2
-

My workaround was forking GitHub repo of Nette Database and reverting the change :P That was the only way to upgrade my app to Nette 2.3.

What problems was it causing? Any chance the removal can be re-considered?

4 years ago

hrach
Member | 1817
+
0
-

@juzna.cz I totally get your feelings. The main point is that the architecture of ndb(t) is pretty messed up and it's not easy to make it right. Some reasons may be found here:

4 years ago

juzna.cz
Member | 249
+
0
-

Thanks for the explanation and links.

The TL;DR answer for my question above would be: Best practice is to use a higher-level library on top of NDB(T), e.g. nextras/orm.

Am I right?

4 years ago

hrach
Member | 1817
+
0
-

Yes, for your usecase, it would be better to use some library like YetOrm. Nextras/ORM is not based on NDB.