3

Background

I have a project that I am working on, and it uses Doctrine 2. I have created the entities, defined the relationships, put test data in the database and am in the testing portion of it. I am not new to Doctrine (because of using Symfony framework), but I am new to using the Doctrine component on it's own -- which is what this is. I don't know if that has any bearing, but I want to make it clear this isn't a Symfony project.

Entities

Interest

/**
 * Class Interest
 * @ORM\Entity()
 * @ORM\Table(name="interest", uniqueConstraints={
 *   @ORM\UniqueConstraint(name="interest_name", columns={"name"})
 * })
 */
class Interest {
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", unique=TRUE, nullable=FALSE)
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="string")
     */
    private $description;

    /**
     * @var array
     *
     * @ORM\OneToMany(targetEntity="Subscription", mappedBy="interests", cascade={"persist", "remove"}, orphanRemoval=TRUE)
     */
    private $subscribers;

    public function __construct() {
        $this->subscribers = new ArrayCollection();
    }

Subscription

/**
 * Class Subscription
 * @ORM\Entity()
 * @ORM\Table(name="subscription")
 */
class Subscription {
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var array
     *
     * @ORM\ManyToOne(targetEntity="Interest", inversedBy="subscribers")
     * @ORM\JoinColumn(name="interest_id", referencedColumnName="id", nullable=FALSE)
     */
    private $interests;

    /**
     * @var string
     *
     * @ORM\ManyToOne(targetEntity="Subscriber", inversedBy="interests")
     * @ORM\JoinColumn(name="subscriber_id", referencedColumnName="id", nullable=FALSE)
     */
    private $subscriber;

    /**
     * @var string
     *
     * @ORM\Column(name="interest_date", type="datetime")
     */
    private $subscribed_on;

    public function __construct() {
        $this->subscribed_on = new \DateTime("now");
        $this->interests = new ArrayCollection();
    }

Subscriber

/**
 * Class Subscriber
 * @ORM\Entity()
 * @ORM\Table(name="subscriber", uniqueConstraints={
 *   @ORM\UniqueConstraint(name="subscriber_email", columns={"email"})
 * })
 */
class Subscriber {
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="first_name", type="string")
     */
    private $first_name;

    /**
     * @var string
     *
     * @ORM\Column(name="last_name", type="string")
     */
    private $last_name;

    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", unique=TRUE, nullable=FALSE)
     */
    private $email;

    /**
     * @var array
     * 
     * @ORM\OneToMany(targetEntity="Subscription", mappedBy="subscriber", cascade={"persist", "remove"}, orphanRemoval=TRUE)
     */
    private $interests;

    /**
     * @var boolean
     *
     * @ORM\Column(name="is_subscribed", type="boolean")
     */
    private $is_subscribed;

    public function __construct() {
        $this->interests = new ArrayCollection();
    }

Testing

Then to test the relationships I am using this simple script:

$subscriptionRepo = $entityManager->getRepository( 'Subscribr\Entity\Subscription' );
$subscriptions = $subscriptionRepo->findAll();

foreach ($subscriptions as $subscription) {
    echo sprintf( "-%s\n", $subscription->getSubscriber()->getEmail() );
    $interests = $subscription->getInterests();
    foreach ($interests as $interest) {
        echo sprintf( "--%s\n", $interest->getName() );
    }
}

Question

The interpreter gets all the way to the inside of the second loop, and fails with this exception:

C:\xampp\htdocs\www\public_html\playground\subscribr>php list_subscriptions.php
-testemail@domain.com
PHP Fatal error:  Call to undefined method Closure::getName() in C:\xampp\htdocs\www\public_html\playground\subscribr\list_subscriptions.php on line 17
PHP Stack trace:
PHP   1. {main}() C:\xampp\htdocs\www\public_html\playground\subscribr\list_subscriptions.php:0

I've done a bunch of Google searches and SO searches and cant figure out why the Subscriber entity renders the email fine, but the Interest entity returns a Closure instead of an iterable containing the interests.

Am I doing something wrong with my relationships in my annotations? Or am I just not understanding how to make the call to get the interests? I am really stuck here. Any help would be great, please let me know if I can post anything else to make it clearer. Thanks!

Edit 1

I made the changes in the relationship so that each subscription can only have one subscriber. When I query the subscription I now get this:

C:\xampp\htdocs\www\public_html\playground\subscribr>php list_subscriptions.php
-testemail@tester.com
--Magazine
-testemail@tester.com
--Newsletter
-testemail@tester.com
--Promotions
-testemail@tester.com
--Email
-testemail2@tester.com
--Magazine
-testemail2@tester.com
--Promotions
-testemail3@tester.com
--Newsletter
-testemail3@tester.com
--Email
-testemail4@tester.com
--Magazine
-testemail4@tester.com
--Promotions
-testemail4@tester.com
--Email
-testemail5@tester.com
--Magazine
-testemail6@tester.com
--Newsletter
-testemail7@tester.com
--Promotions
-testemail9@tester.com
--Promotions
-testemail10@tester.com
--Newsletter

Now I am curious whether its right to combine them, or if that's the interest and subscriber's job. So I'll open another question.

Community
  • 1
  • 1
mrClean
  • 415
  • 5
  • 18
  • The mapping is wrong. Do you need a many to many relationship, between Interest and Subscription? – Vinícius Fagundes Nov 29 '16 at 19:39
  • 1
    I am not completely sure, so I have [posted another question](http://stackoverflow.com/questions/40875478/establishing-relationships-through-a-common-table-the-right-way). For now, your answer solved my issue, and the explanation was great! – mrClean Nov 29 '16 at 20:56

1 Answers1

0

The Problem

In Subscription class you created a ManyToOne relationship. It means multiples subscriptions can have a single interest. But it also means each subscription has one and only one interest. You created it as an array.

/**
 * @var array
 *
 * @ORM\ManyToOne(targetEntity="Interest", inversedBy="subscribers")
 * @ORM\JoinColumn(name="interest_id", referencedColumnName="id", nullable=FALSE)
 */
private $interests;

The Solution

One possible solution to fix this: you need to use a single interest inside a subscription.

/**
 * @var Interest
 *
 * @ORM\ManyToOne(targetEntity="Interest", inversedBy="subscribers")
 * @ORM\JoinColumn(name="interest_id", referencedColumnName="id", nullable=FALSE)
 */
private $interest;

And remove this line, in the costructor.

    $this->interests = new ArrayCollection();

Another way it's to create a ManyToMany relationship. It means a Subscription has multiples Interests and an Interest has multiples Subscriptions. If it's the case I can update this answer creating an example for it.

Vinícius Fagundes
  • 1,983
  • 14
  • 24
  • 1
    I think this is right, but I'm still getting the same Closure exception. How I would like it to work, and I'm not sure if it's set up this way, is like this: a `Subscriber` can have multiple `Interest`s, and each `Interest` can have multiple `Subscriber`s. This will allow me to see which interests a subscriber has, and which interests have been subscribed to. Then, the `Subscription` relates only to one subscriber and one interest. – mrClean Nov 29 '16 at 20:09