0

I'm using PHP 5.3. I have a Vehicle class. These classes have inherited from Vehicle: Car, SUV, Lorry.

I need to get an array holding a specific type of objects inherited from Vehicle. It can be an array of Cars, SUVs, etc...

The way I solved it was that I created a class named CarCollection with a static method, which returns the list of cars. Then a class named SUVCollection for SUV...

But if I were to add a new vehicle class (let's call it Airplane), then I would need to create a new AirPlaneCollection class. Is this a bad choice?

The goal: I'm looking for a way to implement this using only one class, VehicleCollection returns a list of Cars, SUV etc.

How can I know in the code that in this specific script the VehicleCollection::getVehicles() will return cars and not SUV? Maybe I can have some logic the class checking from where it was called, or I send in the caller object as a parameter, then check which class it is and according to that get VehicleCollection::getVehicles() to return for examle SUV and not Car.

4
  • You don't create specialized collection classes but only a single one using the base vehicle class. Everything else is a question of getters and setters. Other languages offer template classes or class templates for such stuff, php does not have such thing. But then again you won't need it in a situation where you want to use php. Commented Mar 27, 2015 at 19:14
  • 2
    You can hold arrays containing instances of different types, each inheriting from Vehicle. Commented Mar 27, 2015 at 19:29
  • I think here you have to use dependency injection - design pattern. May be it will solve your problem. Commented Mar 27, 2015 at 19:33
  • What is collection object responsible for? What does it do? Commented Mar 27, 2015 at 23:20

2 Answers 2

1

You could try making your own collection type, which validates that everything is a vehicle, but also enforces they're all the same type of vehicle.

<?php class Vehicle {} class Car extends Vehicle {} class SUV extends Vehicle {} // This doesn't need to be an SplDoublyLinkedList, it's just // a convenient datastructure to demo with class VehicleCollection extends SplDoublyLinkedList { public function add($index, Vehicle $obj) { $this->validateType($obj); parent::add($index, $obj); } public function push(Vehicle $obj) { $this->validateType($obj); parent::push($obj); } protected function validateType($obj) { // If we have anything in here, ensure next is the same vehicle type if (!($this->isEmpty() || $this->top() instanceof $obj)) { throw new InvalidArgumentException('Argument passed to ' . __CLASS__ . '::' . __FUNCTION__ . ' must all be instances of same type.'); } } } // Make a new collection $col = new VehicleCollection(); // Let's have a couple cars $car = new Car; $car2 = new Car; // And an SUV $suv = new SUV; // Let's add our cars $col->push($car); $col->push($car2); var_dump($col); /* Collection right now: class VehicleCollection#1 (2) { private $flags => int(0) private $dllist => array(2) { [0] => class Car#2 (0) { } [1] => class Car#3 (0) { } } } */ // Now we try to add an SUV $col->push($suv); // and get this: // PHP Fatal error: Uncaught exception 'InvalidArgumentException' with message 'Argument passed to VehicleCollection::validateType must all be instances of same type.' 

This has the added benefit that if you further extended, e.g. made a class SportsCar extends Car {}, that SportsCar could go into your collection.

It was pointed out that I might have been misinterpreting your question. If you're just trying to filter an array, this becomes a much simpler problem. I wouldn't bother to even implement a special class if that's the case - just pass a Closure into array_filter, which is quite readable and an easy pattern to follow elsewhere:

$vehicles = [$car, $suv, $car2]; $cars = array_filter($vehicles, function($vehicle) { return $vehicle instanceof Car; }); $suvs = array_filter($vehicles, function($vehicle) { return $vehicle instanceof SUV; }); 

So in that example, the array of vehicles has an SUV, and once filtered, the $cars array has only the cars. If you want to make that a class method, you could do something along the lines of:

public function getAllOfType($type) { return array_filter( $this->vehicles, function($vehicle) { return is_a($vehicle, $type); } ); } 

Then to grab only cars from your collection:

$cars = $myVehicleCollection->getAllOfType('Car'); 
Sign up to request clarification or add additional context in comments.

3 Comments

The real isue here is that OP wants to retrieve a limited set of items, for instance $collection->getPlains()
Hmm I didn't see that on first reading, but I can understand that interpretation. Didn't really make sense to me why he'd have separate classes defined as "Collections" for that, and as this is a best-practices question I think managing his datatypes like I propose is probably the best approach. I'll update to give an approach to filter
Personaly I would change collection to a multidimensional array with the key being the type of the object added, bit like the other answer but dynamic. This would increase the performance imo because you can just return the collection based on the type rather than the need to foreach every single element and map it correctly
0

What about a static property in the parent class Vehicle that would look something like:

array( 'cars' => array(car instances), 'suvs' =>array(suv instances). .... ) 

Then you would have to put logic in your constructors and destructors to add and remove the objects from this array.

Then you can get a list of all cars by calling Vehicle::thatArry['cars']. But of course you want to throw some nice getters and setters around that array.

I haven't totally thought this through yet, it might need some tweaks. But if its totally off, holla at me.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.