We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

hasOne working, hasMany not working. Changing hasMany to hasOne returns the first record.

This seems simple, but I can't get it to work.

Order Model Init:

    public function initialize()
    {
        $this->setSource("orders");

        $this->hasOne("user_id", "User", "id");
        $this->hasOne("account_id", "Account", "id");
        $this->hasOne("order_type_id", "OrderType", "id");
        $this->hasOne("id", "OrderDistribution","order_id");
        $this->hasMany("id", "OrderDistributionAudience","order_id");
    }

Getting the order:

    public function getOrder() {

        $this->order_type = $this->getOrderType();
        $this->user = $this->getUser();
        $this->account = $this->getAccount();
        $this->order_distribution = $this->getOrderDistribution();
        $this->audiences = $this->getOrderDistributionAudience();

        return $this;
    }

In this case, order_type, user, account, and order_distribution are all returned to the UI just fine. audiences is an empty object. if I change the hasMany relationship to hasOne for AudienceDistributionOrder, I get an object containing the first record. I would expect that hasMany would return an array of AudienceDistributionOrder objects, but it doesn't.



17.5k

My browser is totally locking up in code edit mode in this forum. Anyone else have that problem? (><).

Anyway, I'm doing a json_encode on the $this object to return to the UI via an AJAX call.

This is in my controller:

        $O = Order::findFirst($request->id);
        if ($order = $O->getOrder(0)) {
            $response['success'] = true;
            $response['data']['order'] = $order;
        } else {
            $response['error'] = implode("<br/>",$O->getMessages());
        }
        $this->respond($response);

all $this->respond() does is echo json_encode($response) and die();

And how looks OrderDistributionAudience ?



17.5k

It's basically empty. I have a save method in there, but that's it.



17.5k

It works like this too, but it seems like I shouldn't have to do all of the toArray() crap.

    public function getOrder() {

        $order_type = $this->getOrderType();
        $user = $this->getUser();
        $account = $this->getAccount();
        $order_distribution = $this->getOrderDistribution();
        $auds = $this->getOrderDistributionAudience();

        $ODA = new OrderDistributionAudience();
        $audiences = $ODA->getAudienceInfo($auds);

        $order = $this->toArray();
        $order['user'] = $user->toArray();
        $order['account'] = $account->toArray();
        $order['order_type'] = $order_type->toArray();
        $order['order_distribution'] = $order_distribution->toArray();
        $order['audiences'] = $audiences;

        return $order;
    }

Where is getOrder() defined? In a controller or a model?

And yes, code editing does lock up the browser quite effectively. It's been a bug for quite some time. I've noticed it's worst on Firefox - Chrome seems better. Personally I just do my code editing in SublimeText then paste it in.



17.5k

getOrder is in the Order model and is called by a controller. I have other things I want to add to the order that aren't in the database, so that's why doing a simple find won't suffice.

That's weird that a hasOne() relationship maps, but a hasMany() doesn't. What is the class of the empty object?

Do you have a query log enabled? Would you be able to see what queries are actually being run when retrieving the audiences?

There are a number of things you could be doing differently/easier. This doesn't necessarily answer your initial question, but it would get you more inline with what are considered best practices:

  1. Rather than building getOrder(), look into afterFetch(). This method is called automatically after a record is retrieved, and is the perfect place to massage the data.
  2. You shouldn't need to assign related models to a variable. The relationship itself will do that. For example, you've defined this:
$this->audiences = $this->getOrderDistributionAudience();

but that won't be necessary if you simply set up the relationship with an alias like so:

$this->hasMany("id", "OrderDistributionAudience","order_id",['alias'=>'audiences']);

You can then do this:

$Order = Order::findFirst(0);
do_something_with($Order->audiences);


17.5k
edited Feb '16

Class of $this->getOrderDistributionAudience() when the hasMany relationship is defined is Phalcon\Mvc\Model\Resultset\Simple. Seems correct, but when it does the json_encode on the return, it ends up as an empty object in the return of the AJAX call.

Sorry, no query logger. I'm not sure how I would use that.

Thanks for the tip on afterFetch and aliases. I'll probably use that a lot.

Current code is:

Order.php model:

    public function initialize()
    {
        $this->setSource("orders");
        $this->hasOne("user_id", "User", "id",['alias'=>'user']);
        $this->hasOne("account_id", "Account", "id",['alias'=>'account']);
        $this->hasOne("order_type_id", "OrderType", "id",['alias'=>'order_type']);
        $this->hasOne("id", "OrderDistribution","order_id",['alias'=>'detail']);
        $this->hasMany("id", "OrderDistributionAudience","order_id",['alias'=>'audiences']);
    }

And the getOrder call is gone in favor of this:

    public function afterFetch() {
        $this->order_type = $this->order_type;
        $this->user = $this->user;
        $this->account = $this->account;
        $this->detail = $this->detail;
        $this->audiences = $this->audiences;
    }

Controller call is pretty simple too:

    public function orderdistributiongetAction() {
        $response = $this->getResponse();
        $request = $this->getRequest();
        if (empty($request->id)) {
            $response['error'] = "Order not found";
            $this->respond($response);
        }
        $O = Order::findFirst($request->id);
        $response['success'] = true;
        $response['data']['order'] = $O;
        $this->respond($response);
    }

Same result as before. 'audiences' comes through as an empty object in the json response

Your afterFetch() method is unnecessary - it doesn't actually do anything.

Does $O->getMessages() return anything useful?

I don't see how it could be, but might it be your database? Could you paste the structure of your relevant tables?



17.5k

If I don't put that code in the afterFetch method, I don't get the user, account, order_type, etc. data returned to the UI. All I get is the order object and properties local to that object.

$O->getMessages() is NULL.

Here's a picture of the order tables.

https://www.dropbox.com/s/4btolfisexxc7yp/phalcon_db.jpg?dl=0



17.5k

...and thanks for your help, btw. I know the original problem hasn't been solved yet, but you've given me ideas to make the code better.

If I don't put that code in the afterFetch method, I don't get the user, account, order_type, etc. data returned to the UI. All I get is the order object and properties local to that object.

Have you explicitely defined, for example, Order::$account like this?

class Order extends \Phalcon\Mvc\Model{

    public $account;
    // ... etc
}

If so, that's the problem (at least the problem of the data not being returned to the UI). Setting up the aliases causes \Phalcon\Mvc\Model's magic getters to trigger when you ask for $Order->account. If you've defined Order::$account like above, then the magic getters never get triggered.

As for your original question: I'm sure the solution will end up being fairly trivial. Personally, if I were in your situation I would set up a query log and see what is actually being sent to the database, then work back. If you don't have root access to your server, it'll be tough to set up a MySQL general query log. The Phalcon documentation does show you how to do it in code: https://docs.phalcon.io/en/latest/reference/models.html#logging-low-level-sql-statements

Well i guess if he is running phalcon he should have mysql logs. But even if i have them i prefer phalcon mysql log query.

If I don't put that code in the afterFetch method, I don't get the user, account, order_type, etc. data returned to the UI. All I get is the order object and properties local to that object.

Have you explicitely defined, for example, Order::$account like this?

class Order extends \Phalcon\Mvc\Model{

  public $account;
  // ... etc
}

If so, that's the problem (at least the problem of the data not being returned to the UI). Setting up the aliases causes \Phalcon\Mvc\Model's magic getters to trigger when you ask for $Order->account. If you've defined Order::$account like above, then the magic getters never get triggered.

As for your original question: I'm sure the solution will end up being fairly trivial. Personally, if I were in your situation I would set up a query log and see what is actually being sent to the database, then work back. If you don't have root access to your server, it'll be tough to set up a MySQL general query log. The Phalcon documentation does show you how to do it in code: https://docs.phalcon.io/en/latest/reference/models.html#logging-low-level-sql-statements