I'm trying to get a multi-dimensional array from an Entity.
Symfony Serializer can already convert to XML, JSON, YAML etc. but not to an array.
I need to convert because I want have a clean var_dump. I now have entity with few connections and is totally unreadable.
How can I achieve this?
You can actually convert doctrine entities into an array using the built in serializer. I actually just wrote a blog post about this today:
https://skylar.tech/detect-doctrine-entity-changes-without/
You basically call the normalize function and it will give you what you want:
$entityAsArray = $this->serializer->normalize($entity, null);
I recommend checking my post for more information about some of the quirks but this should do exactly what you want without any additional dependencies or dealing with private/protected fields.
Apparently, it is possible to cast objects to arrays like following:
<?php
class Foo
{
public $bar = 'barValue';
}
$foo = new Foo();
$arrayFoo = (array) $foo;
var_dump($arrayFoo);
This will produce something like:
array(1) {
["bar"]=> string(8) "barValue"
}
If you have got private and protected attributes see this link : https://ocramius.github.io/blog/fast-php-object-to-array-conversion/
Get entity in array format from repository query
In your EntityRepository you can select your entity and specify you want an array with getArrayResult() method.
For more informations see Doctrine query result formats documentation.
public function findByIdThenReturnArray($id){
$query = $this->getEntityManager()
->createQuery("SELECT e FROM YourOwnBundle:Entity e WHERE e.id = :id")
->setParameter('id', $id);
return $query->getArrayResult();
}
If all that doesn't fit you should go see the PHP documentation about ArrayAccess interface.
It retrieves the attributes this way : echo $entity['Attribute'];
PHP 8 allows us to cast object to array:
$var = (array)$someObj;
It is important for object to have only public properties otherwise you will get weird array keys:
<?php
class bag {
function __construct(
public ?bag $par0 = null,
public string $par1 = '',
protected string $par2 = '',
private string $par3 = '')
{
}
}
// Create myBag object
$myBag = new bag(new bag(), "Mobile", "Charger", "Cable");
echo "Before conversion : \n";
var_dump($myBag);
// Converting object to an array
$myBagArray = (array)$myBag;
echo "After conversion : \n";
var_dump($myBagArray);
?>
The output is following:
Before conversion :
object(bag)#1 (4) {
["par0"]=> object(bag)#2 (4) {
["par0"]=> NULL
["par1"]=> string(0) ""
["par2":protected]=> string(0) ""
["par3":"bag":private]=> string(0) ""
}
["par1"]=> string(6) "Mobile"
["par2":protected]=> string(7) "Charger"
["par3":"bag":private]=> string(5) "Cable"
}
After conversion :
array(4) {
["par0"]=> object(bag)#2 (4) {
["par0"]=> NULL
["par1"]=> string(0) ""
["par2":protected]=> string(0) ""
["par3":"bag":private]=> string(0) ""
}
["par1"]=> string(6) "Mobile"
["�*�par2"]=> string(7) "Charger"
["�bag�par3"]=> string(5) "Cable"
}
This method has a benefit comparing to Serialiser normalizing -- this way you can convert Object to array of objects, not array of arrays.
I had the same issue and tried the 2 other answers. Both did not work very smoothly.
The $object = (array) $object; added alot of extra text in my key
names.
The serializer didn't use my active property because it did not have is in front of it and is a boolean. It also changed the sequence of my data and the data itself.
So I created a new function in my entity:
/**
* Converts and returns current user object to an array.
*
* #param $ignores | requires to be an array with string values matching the user object its private property names.
*/
public function convertToArray(array $ignores = [])
{
$user = [
'id' => $this->id,
'username' => $this->username,
'roles' => $this->roles,
'password' => $this->password,
'email' => $this->email,
'amount_of_contracts' => $this->amount_of_contracts,
'contract_start_date' => $this->contract_start_date,
'contract_end_date' => $this->contract_end_date,
'contract_hours' => $this->contract_hours,
'holiday_hours' => $this->holiday_hours,
'created_at' => $this->created_at,
'created_by' => $this->created_by,
'active' => $this->active,
];
// Remove key/value if its in the ignores list.
for ($i = 0; $i < count($ignores); $i++) {
if (array_key_exists($ignores[$i], $user)) {
unset($user[$ignores[$i]]);
}
}
return $user;
}
I basicly added all my properties to the new $user array and made an extra $ignores variable that makes sure properties can be ignored (in case you don't want all of them).
You can use this in your controller as following:
$user = new User();
// Set user data...
// ID and password are being ignored.
$user = $user->convertToArray(["id", "password"]);
Related
PHP 8.1 is almost getting released, including support for Enumerations. I was testing some of the enum functionality and couldn't find much documentation about it. Hence my question: how do I get all values of an enum?
For basic enums:
$suits = array_column(Suit::cases(), 'name');
For backed enums where you want the values:
$suits = array_column(Suit::cases(), 'value');
You could then do something like this:
trait EnumToArray
{
public static function names(): array
{
return array_column(self::cases(), 'name');
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
public static function array(): array
{
return array_combine(self::values(), self::names());
}
}
enum Suit: string
{
use EnumToArray;
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
Suit::array() will return:
Array
(
[H] => Hearts
[D] => Diamonds
[C] => Clubs
[S] => Spades
)
After some research I found the answer. You can use the static method: cases().
enum Status
{
case PAID;
case Cancelled;
}
Status::cases();
The cases method will return an array with an enum (UnitEnum interface) for each value.
Need the values and not Enum instances?
I've written a Composer package for this, othyn/php-enum-enhancements, as the UnitEnum::cases() method wasn't what I was looking for, as that returns an array of MySuperCoolEnum instances instead of the underlying values as their raw type, which is what I wanted.
Its a trait that can be easily added to any enum that does the following:
Adds a new static UnitEnum::valueArray(): array method that returns all values within an Enum as an equally typed array of Enum values
Adds a new static UnitEnum::valueList(string $separator = ', '): string method that returns all values within an Enum as a comma separated list string
In which produces the following for normal Enum's:
<?php
namespace App\Enums;
use Othyn\PhpEnumEnhancements\Traits\EnumEnhancements;
enum TestEnum
{
use EnumEnhancements;
case Alpha;
case Bravo;
case Charlie;
case Delta;
case Echo;
}
var_dump(TestEnum::valueArray());
// Results in the following being printed:
// array(5) {
// [0]=>
// string(5) "Alpha"
// [1]=>
// string(5) "Bravo"
// [2]=>
// string(7) "Charlie"
// [3]=>
// string(5) "Delta"
// [4]=>
// string(4) "Echo"
// }
var_dump(TestEnum::valueList());
// Results in the following being printed:
// string(34) "Alpha, Bravo, Charlie, Delta, Echo"
var_dump(TestEnum::valueList(separator: ':'));
// Results in the following being printed:
// string(30) "Alpha:Bravo:Charlie:Delta:Echo"
... and the following for Backed Enum's, the following being a string example:
<?php
namespace App\Enums;
use Othyn\PhpEnumEnhancements\Traits\EnumEnhancements;
enum TestStringBackedEnum: string
{
use EnumEnhancements;
case Alpha = 'alpha';
case Bravo = 'bravo';
case Charlie = 'charlie';
case Delta = 'delta';
case Echo = 'echo';
}
var_dump(TestStringBackedEnum::valueArray());
// Results in the following being printed:
// array(5) {
// [0]=>
// string(5) "alpha"
// [1]=>
// string(5) "bravo"
// [2]=>
// string(7) "charlie"
// [3]=>
// string(5) "delta"
// [4]=>
// string(4) "echo"
// }
var_dump(TestStringBackedEnum::valueList());
// Results in the following being printed:
// string(34) "alpha, bravo, charlie, delta, echo"
var_dump(TestStringBackedEnum::valueList(separator: ':'));
// Results in the following being printed:
// string(30) "alpha:bravo:charlie:delta:echo"
... and yes it works on int's too!
There are more examples in the Usage part of the package's README.
In addition to UnitEnum::cases() you can use ReflectionEnum with this
$reflection = new ReflectionEnum(Status::class);
$reflection->getCases();
note that in both cases you will not be able to get the enum methods. but as long as the ReflectionEnum is extending the ReflectionClass so you can use the rest of ReflectionClass methods such as getMethods
I think the best options is using a trait for that.
For example:
EnumsToArray.php
<?php
namespace App\Traits;
trait EnumsToArray {
public static function toArray(): array {
return array_map(
fn(self $enum) => $enum->value,
self::cases()
);
}
}
And later, in you enum you should have:
use App\Traits\EnumsToArray;
Enum Currency: string {
use EnumsToArray;
case DOLLAR = "usd";
case EURO = "eur";
}
I have used the following in my project;
public static function toAssociativeArray(): array
{
foreach(self::cases() as $case) {
$array[$case->value] = $case->name;
}
return $array;
}
Which results in an associative array like this;
using strings as values
enum DiaryRole: string
{
case DANGER = 'red';
case WARNING = 'yellow';
case SAFE = 'green';
}
$array = [
'red' => 'DANGER',
'yellow' => 'WARNING',
'green' => 'SAFE'
];
or when using integers as values
enum DiaryRole: int
{
case DANGER = 1;
case WARNING = 2;
case SAFE = 3;
}
$array = [
1 => 'DANGER',
2 => 'WARNING',
3 => 'SAFE'
];
You can now use the array to get any information you need, and you can get only the columns or values using array_keys() or array_values()
I have used this code to easily foreach through them in a form select
I wrapped a slitly changed approach from #Michael up in a small package, cause I needed it in multiple projects:
https://github.com/laracraft-tech/laravel-useful-traits#usefulenums
Install via composer:
composer require laracraft-tech/laravel-useful-traits
This is how it is working:
use LaracraftTech\LaravelUsefulTraits\UsefulEnums;
enum PaymentType: int
{
use UsefulEnums;
case Pending = 1;
case Failed = 2;
case Success = 3;
}
PaymentType::names(); // return ['Pending', 'Failed', 'Success']
PaymentType::values(); // return [1, 2, 3]
PaymentType::array(); // return ['Pending' => 1, 'Failed' => 2, 'Success' => 3]
all. I am working on a custom-build API built in PHP. I have an array (see below) that has been returned from my database access object. When I use the json_encode function, the int value assigned to phone1Type gets encoded incorrectly. I have tried this with multiple records, and the value in the encoded json object always matches the phone2Type. Any ideas as to what might be going on? (I've included two sample arrays below in addition to their corresponding json object.)
The code that I'm using to check the array and json values is the following:
$responseObject = $userCtrl->selectPersonForUserId($userId);
var_dump($responseObject);
var_dump(json_encode($responseObject));
One example array to encode is as follows. (The phone1Type and phone2Type keys are at the very end, but include the full array here for completeness. Also, as a side note, the other int values in the array are encoding fine.)
object(Adult)#13 (8) {
["person":protected]=>
object(Person)#14 (4) {
["id":protected]=>
int(3)
["firstName":protected]=>
string(7) "William"
["lastName":protected]=>
string(3) "Smith"
["hasVerified":protected]=>
bool(false)
}
["address":protected]=>
object(Address)#17 (4) {
["id":protected]=>
int(2)
["address1":protected]=>
string(15) "520 Hilbert Dr."
["address2":protected]=>
string(0) ""
["city":protected]=>
object(City)#18 (3) {
["zip":protected]=>
string(5) "71342"
["city":protected]=>
string(11) "West Monroe"
["state":protected]=>
string(2) "AL"
}
}
["email":protected]=>
string(14) "wmrmay#spam.com"
["phone1":protected]=>
string(10) "6195080000"
["phone1Type":protected]=>
int(1)
["phone2":protected]=>
string(10) "3188126574"
["phone2Type":protected]=>
int(0)
["teacher":protected]=>
NULL
}
This encodes to the following json object:
{"person":{"id":3,"firstName":"William","lastName":"Smith","hasVerified":false},"address":{"id":2,"address1":"520 Hilbert Dr.","address2":"","city":{"zip":"71342","city":"West Monroe","state":"AL"}},"email":"wmrmay#spam.com","phone1":"6195080000","phone1Type":0,"phone2":"3188126574","phone2Type":0,"teacher":null}
For brevity, here's the last few lines of another array followed by its json counterpart:
["email":protected]=>
string(20) "wltrallen2#gmail.com"
["phone1":protected]=>
string(10) "6192047586"
["phone1Type":protected]=>
int(1)
["phone2":protected]=>
NULL
["phone2Type":protected]=>
NULL
["teacher":protected]=>
NULL
"email":"wltrallen2#gmail.com","phone1":"6192047586","phone1Type":null,"phone2":null,"phone2Type":null,"teacher":null}
Edited to add original Adult.php model class:
class Adult implements JsonSerializable {
protected $person; // Person object
protected $address; // Address object
protected $email;
protected $phone1;
protected $phone1Type; // PhoneType object
protected $phone2;
protected $phone2Type; // PhoneType object
protected $teacher; // Teacher object
public function __construct($person, $address, $email, $phone1, $phone1Type, $phone2, $phone2Type, $teacher)
{
$this->person = $person;
$this->address = $address;
$this->email = $email;
$this->phone1 = $phone1;
$this->phone1Type = $phone1Type;
$this->phone2 = $phone2;
$this->phone2Type = $phone2Type;
$this->teacher = $teacher;
}
... // Getters and Setters removed for brevity
private function getPhoneType($type) {
if(PhoneTypes::isValid($type)) {
return PhoneTypes::StringDict[$type];
}
return '';
}
function jsonSerialize() {
$array = [
'person' => $this->person,
'address' => $this->address,
'email' => $this->email,
'phone1' => $this->phone1,
'phone1Type' => $this->phone2Type,
'phone2' => $this->phone2,
'phone2Type' => $this->phone2Type,
'teacher' => $this->teacher
];
return $array;
}
}
So, embarrassingly, this was just caused by an initial typo and then forgetfulness on my part.
In the Adult.php model class, I has implemented the JsonSerializable interface to make sure that an Adult object could be encoded in Json. When doing so, I made a typographical error when building the array:
'phone1Type' => $this->phone2Type,
which should have been, of course...
'phone1Type' => $this->phone1Type,
This typo was the source of my issue. Ugh!
However, as I've been deeply mired in this project and it had been some time since I originally built those model classes, I had completely forgot that for an object to be encodable by JSON that it had to implement the JsonSerializable interface. So, as I was debugging, it never occurred to me to look back in my model at the very end of the file to examine the jsonSerialize() function. (Insert face palm here.)
Thank you for your responses. This is the first time that I've actually gotten to post a question on stackOverflow, and I appreciate you all taking a look. Sorry that it wasn't an exciting question and merely a silly novice programmer moment.
I'm trying to cast an entity property into an array so that it autoserializes.
The Entity is set up as follows
\App\Entities\Submission.php
<?php
namespace App\Entities;
use CodeIgniter\Entity;
class Submission extends Entity
{
protected $casts =[
'field2' => 'array'
];
}
Then in a controller I create a new entity, filling it using the constructor
<?php
$allowedFromPost = [
'field1'=>'value1',
'field2'=>[0,1],
];
$submission = new \App\Entities\Submission($allowedFromPost);
?>
Dumping the submission at this point (var_dump()) shows field2 being an array, it's not serialised.
["attributes":protected]=>
array(2) {
["field1"]=>
string(6) "value1"
["field2"]=>
array(2) {
[0]=>
int(0)
[1]=>
int(1)
}
}
if I do
$allowedFromPost = [
'field1'=>'value1',
'field2'=>[0,1],
];
$submission = new \App\Entities\Submission($allowedFromPost);
$submission->field2 = $submission->field2;
and then var_dump, field2 is correctly serialised.
["attributes":protected]=>
array(2) {
["field1"]=>
string(6) "value1"
["field2"]=>
string(22) "a:2:{i:0;i:0;i:1;i:1;}"
}
For some reason, it seems like filling using the constructor does not autoserialize, I have to manually set the field. Am I doing something wrong?
The problem this caused is that when I tried inserting that entity into the Database, it threw an error saying
"mysqli_sql_exception Operand should contain 1 column(s)"
which went away when I flattened the array (first by dropping all but one values to test and then by using what I've done above)
================
EDIT 11/05: This turned out to be an issue on the core code. fill() (and the constructor) were not configured to use __set() so autoserialization was not happening. See the PR on Codeigniter's Github page here.
I'll be accepting Kulshreshth K's answer because, for now, this provides an adequate workaround but in the future it will most likely not be needed.
Add this in your Entity:
public function __construct($arr) {
$this->field2 = $arr['field2'];
}
Controller:
$allowedFromPost = [
'field1'=>'value1',
'field2'=>[0,1],
];
$submission = new \App\Entities\Submission($allowedFromPost);
var_dump($submission)
Result:
["attributes":protected]=>
array(2) {
["field1"]=>
string(6) "value1"
["field2"]=>
string(22) "a:2:{i:0;i:0;i:1;i:1;}"
}
As per CI documentation you need to set it after initialize its model anyway:
<?php namespace App\Entities;
use CodeIgniter\Entity;
class User extends Entity
{
protected $casts => [
'options' => 'array',
'options_object' => 'json',
'options_array' => 'json-array'
];
}
$user = $userModel->find(15);
$options = $user->options;
$options['foo'] = 'bar';
$user->options = $options;
$userModel->save($user);
I have the following issue. I have just created a new mongodb instance in my aws account with the same version but the results coming from my php queries come in a different way.
Before I used to get an array of objects but now I get and array of arrays.
For example :
Before:
array(1) {
[0] =>
class stdClass#401 (6) {
public $_id =>
class MongoDB\BSON\ObjectId#390 (1) {
public $oid =>
string(24) "5a685fa82fdc5d031e25451c"
}
}
}
Now:
array(1) {
[0]=>
object(stdClass)#393 (6) {
["_id"]=>
object(MongoDB\BSON\ObjectId)#390 (1) {
["oid"]=>
string(24) "5a685fa82fdc5d031e25451c"
}
}
}
Before I used to access my variables from php using the arrow like :
$document->_id;
but now, after getting the results in that way, I need to get change everything to :
$document['_id'];
Is there any setting (php/mongo server) so I can get the results like previously?
I use the php mongo driver to query my database - e.g :
$query = new Query($filter, $options);
/** #var MongoDB\Driver\Manager $manager */
return $manager->executeQuery($collection,$query)->toArray();
Thank you
In the cursor you can specify how it returns the results with setTypemap. I think ['document' => 'stdClass'] will do the trick:
$query = new Query($filter, $options);
/** #var MongoDB\Driver\Manager $manager */
$cursor = $manager->executeQuery($collection, $query);
$cursor->setTypeMap(['document' => 'stdClass']);
return $cursor->toArray();
$cursor->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']);
I'm sorry if my question already asked before. I don't know what the search term that match my problem. I've searched 'OOP child', 'PHP object child', etc but I got no clue at all.
So, here we go. I wanted to make something like this:
$school = new School;
var_dump($school);
And resulting something like this:
object(School)#1 (2) {
["name"]=>
string(14) "All Way School"
["object"]=>
object(School)#2 (2) {
["art"]=>
object(School)#3 (1) {
["student"]=>
int(25)
}
["law"]=>
object(School)#4 (1) {
["student"]=>
int(30)
}
}
}
See the output. object has 2 child: art and law, each has a child student. This way, I can access law's total students by $school->object->law->student. But, I don't know what to write in my class. All I know I can only make $school->name in my School class like this:
class School {
$name = "All Way School";
}
I don't know how to make $object and its child.
Note: I'm just guessing the output. I'm really don't understand how to get it. I'm inspired by SimpleXMLElement.
Edit: I'm replacing $class with $object to prevent confusion.
The OOP 'way' would be to have a different object for each School, Subject (you can't use Class, as it's a reserved keyword) and Student. Define them as normal, with specific properties:
Class School {
// methods, vars etc
}
Class Subject {
// ...
}
Then, you can create new instances of Class within School and assign them to a variable:
Class School {
var $subjects;
function __construct() {
$this->subjects = new Array();
}
}
$mySchool = new School();
$mySchool->subjects[] = new Subject();
Then you can get the subjects using:
$mySchool->subjects[1]->name;
Hope that helps
You can make a child the same way you assigned a name:
class School {
$name = "All Way School";
public function __construct() { // executed when creating a new School object
$this->classes = array(
'art' => new SchoolClass('art'),
'law' => new SchoolClass('law')
);
}
}
You can do the same for the SchoolClass class, making it contain all the students;
class SchoolClass {
$name;
public function __construct( $name ) {
$this->name = $name; // law, art, etc
$this->students = array(
new SchoolStudent( 2 ),
new SchoolStudent( 5 )
);
}
}
(Note that you would usually not put the students hard-coded in the constructor like this, but rather load them from a data source.)
Then count the students for instance:
$School = new School();
echo count( $School->classes['law']->students );
class School {
public $name, $class;
public function __construct() {
$this->class = new SchoolClass();
$this->name = "All Way School";
}
}
class SchoolClass {
public $art, $law;
public function __construct() {
$this->art = new SchoolStudent(25);
$this->law = new SchoolStudent(30);
}
}
class SchoolStudent {
public $student;
public function __construct($student) {
$this->student = $student;
}
}
$school = new School();
var_dump($school);
var_dump($school->class->law->student);
Will give you exactly this:
object(School)#1 (2) {
["name"]=>
string(14) "All Way School"
["class"]=>
object(SchoolClass)#2 (2) {
["art"]=>
object(SchoolStudent)#3 (1) {
["student"]=>
int(25)
}
["law"]=>
object(SchoolStudent)#4 (1) {
["student"]=>
int(30)
}
}
}
int(30)
If you don't want to type every class by hand, read about overloading in PHP and beautiful __set()/__get() methods.
Typecasting can be other solution that fits:
$school = (object) array(
"name" => "All Way School",
"object" => (object) array(
"art" => (object) array("student" => 25),
"law" => (object) array("student" => 30),
),
);
var_dump($school);
var_dump($school->object->law->student);
Which gives you
object(stdClass)#4 (2) {
["name"]=>
string(14) "All Way School"
["object"]=>
object(stdClass)#3 (2) {
["art"]=>
object(stdClass)#1 (1) {
["student"]=>
int(25)
}
["law"]=>
object(stdClass)#2 (1) {
["student"]=>
int(30)
}
}
}
int(30)
I understand that all this is not what you're looking for, but I hope that these examples will help you understand your own needs better and ask the right question. Your current question is vague and unclear: even if I gave you literally what you've asked, you ain't going to accept my answer.
You need a seperate class for each object, they can;t all be instances of School. For example, Your class School would have two properties, name and subjects. Then you would have an class called subject which contains a name and a numbver of students. Note that I have used the word 'subject' instead of 'class' - this is because 'class' is a reserved word in PHP and even if it weren't, it would get very confusing if you had a class called 'class'.
So you might do something like this:
<?php
class School {
public $name;
public $subjects = array();
function __construct ($name) {
$this->name = $name;
}
function addSubject ($subject) {
// Adds a new subject object
if (is_string($subject)) $subject = new Subject($subject);
$this->subjects[$subject->name] = $subject;
}
function getSubjectByName ($name) {
// returns a Subject object or FALSE on failure
return (isset($this->subjects[$name])) ? $this->subjects[$name] : FALSE;
}
function getStudentsBySubjectName ($name) {
// returns number of students for a subject or FALSE on failure
return (isset($this->subjects[$name])) ? $this->subjects[$name]->numberOfStudents : FALSE;
}
// ... more methods
}
class Subject {
public $name;
public $numberOfStudents;
function __construct ($name, $students = 0) {
$this->name = $name;
$this->numberOfStudents = $students;
}
// ... more methods
}
$school = new School('Useless City High');
$school->addSubject(new Subject('Math', 2));
$school->addSubject('Stupidity Studies');
$school->subjects['Stupidity Studies']->numberOfStudents = 65;
$school->addSubject(new Subject('Law'));
var_dump($school);
$math = $school->getSubjectByName('Math');
echo $math->numberOfStudents; // outputs '2'
echo $school->getStudentsBySubjectName('Math'); // outputs '2'
$stupidity = $school->subjects['Stupidity Studies'];
echo $stupidity->numberOfStudents; // outputs '65'
echo $school->getStudentsBySubjectName('Stupidity Studies'); // outputs '65'
echo $school->getStudentsBySubjectName('Law'); // outputs '0'
echo $school->subjects['Law']->numberOfStudents; // outputs '0'
?>
While you could write this in a single class that contains more instances, it doesn't make semantic sense to do so in this situation.
A SimpleXML element is one object that contain children that are instances of itself. This is because that is how XML works - any element can contain any other element (within reason). E.g. you can have <div><div><div>Some Data</div></div></div> but equally you could just have <div>Some Data</div>. All those divs are XML elements - they are all the same type of object. They can potentially contain more of themselves, but this situation is rare.
You are dealing with 2 distinct types of object - a School and a Subject. Think of it like the real world - a School won't contain another School, a Suject wont contain another Subject, and a Subject will not exist outside a School.
You could take the above example a step further, and create a class called Student since this is another distict type. A Student might be contained by a School or a Subject, but it will never contain either of them. Equally, say you had a Book class, a Student could have one, a Subject could have one, or a School could have one. But a Book would never contain a whole School - it just doesn't make sense.
OOP is all about objects. Think about it in terms of the real world, it will help you make sense of what classes you need, and how they can interact with each other.