How to get position value from a model (catalog/product_image) in Magento? - image

I only have this Magento model (catalog/product_image). Is there any way for me to get position value or label value of that image. When I var dump this model, I can only get width, height, quality, _keepAspectRatio, etc. The most valuable data I can get from it is image file name like this: /s/e/productABC.png

Im not quite sure if you will get anything more useful from the catalog/product_image model. From your question it sounds like you are wanting to fetch the image label. If you have access to a product object you can use:
$_product->getMediaGalleryImages()->getItemByColumnValue('label', 'LABEL_NAME')->getUrl();
This will return you the label of the image.
If you need any more help please share what objects you have access to and what you are trying to achieve.

in that class there is just one property and one set and one get function for file
Property : protected $_baseFile;
Function
/**
* Set filenames for base file and new file
*
* #param string $file
* #return Mage_Catalog_Model_Product_Image
*/
public function setBaseFile($file)
and
public function getBaseFile()
{
return $this->_baseFile;
}
This implies: You will not get position using this object.
Just check getBaseFile that will return a string.
I always suggest that for any given object if extended from varien_object use $object->debug(); as this provides lot of information on what all it stores.

Related

Automatic filter on attribute depending on authenticated user

I have an entity called event, the event can have many rooms and a room can have many participants.
If I access all events (with a specific user) I can filter events where the user has no access right (no room with a connection to the specific user) by using extensions.
That works fine.
The response contains all events which have at least one room with access rights.
But If the event has multiple rooms and the user has only access to one room. The response includes both rooms. I created a RoomExtension, but this class will not be invoked.
Thanks
Your problem is caused by the fact that filters and extensions only work on the query that retrieves the primary entities. The related entities are retrieved using Doctrines associations wich are part of the domain model that is meant to be the single source of truth for all purposes. What you need is a user-specic view on that model, which in the context of api's usually consists of DTOs.
I think there are basically two solutions:
Query primarily for Events and convert the into EventDTOs, then either query for - or filter out - the related Rooms,
Query primarily for Rooms, then group them into EventDTOs.
I explain the second solution here because i guesss that it is simpeler and it shoud make your RoomExtension work out of the box, which makes it the better fit to your question, but also because i happen to have built and tested something similar in a tutorial so it is a lot less work to write an answer with confidence.
The downside of this solution is that it does not support pagination.
Bucause this solution primarily queries for Rooms, the the operation is on the Room resource. If it where the only collectionOperation of Room it could be like this:
(..)
* #ApiResource(
* collectionOperations={
* "get_accessible_events"={
* "method"="GET",
* "path"="/rooms/accessible-events",
* "output"=EventDTO::class,
* "pagination_enabled"=false
* }
* }
* }
*/
class Room {
(..)
(This does not have to be your only collectionOperation, you can still have "get", "post" and others).
Right now this still produces a flat collection of Rooms, you need to group them into EventDTOs. The DTOs page of the docs suggest to make a DataTransformer to produce the DTOs, but that only works if your DTOs are one to one with the entities retrieved by the query. But a CollectionDataProvider can do the trick. Because you do not need to adapt the query itself you can simply decorate the default CollectionDataProvider service:
namespace App\DataProvider;
use ApiPlatform\Core\Api\OperationType;
use App\DTO\EventDTO;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Entity\Room;
class RoomAccessibleEventCollectionDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
{
/** #var CollectionDataProviderInterface */
private $dataProvider;
/**
* #param CollectionDataProviderInterface $dataProvider The built-in orm CollectionDataProvider of API Platform
*/
public function __construct(CollectionDataProviderInterface $dataProvider)
{
$this->dataProvider = $dataProvider;
}
/**
* {#inheritdoc}
*/
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return Room::class === $resourceClass
&& $operationName == 'get_accessible_events';
}
/**
* {#inheritdoc}
*/
public function getCollection(string $resourceClass, string $operationName = null, array $context = []): array
{
$rooms = $this->dataProvider->getCollection($resourceClass, $operationName, $context);
$dtos = [];
foreach ($rooms as $room) {
$key = $room->getId();
if (isset($dtos[$key])) {
$dtos[$key]->addRoom($room);
} else {
$dto = new EventDTO($room->getEvent());
$dto->addRoom($room);
$dtos[$key] = $dto;
}
}
return $dtos;
}
}
You do need to configure the service in config/services.yaml:
'App\DataProvider\RoomAccessibleEventCollectionDataProvider':
arguments:
$dataProvider: '#api_platform.doctrine.orm.default.collection_data_provider'
This does not replace the default CollectionDataProvider but adds another one that gets the default one injected.
I guess you can make the EventDTO class yourself now. Then it should work. Filters defined on Room will also work as usual, for example if rooms can be filtered by the date of their event ?event.date[gte]=2020-10-10 will only find rooms with events on or after 2020-10-10 and return their EventDTO's.
However, in the swagger docs the get_accessible_events operations summary and descriptions still come from Room. You can look up how to add a SwaggerDecorator in the docs or take a look at the chapter9-api branch of the tutorial. The latter also contains complete explanations and tested code for entities, the DTO (Report Model) and an extension for only showing data the user is authorized for, but is not taylored to your questions and would all together be way beyond what a to the point answer.
I can not give you any more hints on this site with respect the other solution because this site will probably see them as an incomplete or unclear answer and punish me for it.

Image and name as title

I am building a Laravel Nova application and want to show a user with image and name.
/**
* The single value that should be used to represent the resource when being displayed.
*
* #var string
*/
public static $title = 'name';
As you can see above, the $title is used to represent the resource, but I want two values, a image (Avatar) and name (Sting).
Is this possible, or is it possible to show an other value like an image, instead of a string?
Use Avatar field
If a resource contains an Avatar field, that field will be displayed next to the resource's title when the resource is displayed in search results
https://nova.laravel.com/docs/1.0/resources/fields.html#field-types

Laravel : Polymorphic Relations + Accessor

I have a Gallery table that uses Polymorphic Relations so I can add Images and Videos to my gallery list.
Within the Gallery table I have a galleryable_type column that is populated with either App\Video or App\Image.
Is there a way for me to use an accessor (docs here) to change the value of galleryable_type to either video or image so I can use that column in JS to decide what gallery item type I'm dealing with?
I tried the following:
/**
* Get and convert the makeable type.
*
* #param string $value
* #return string
*/
public function getMakeableTypeAttribute($value)
{
return str_replace('app\\', '', strtolower($value));
}
But i end up with the following error:
FatalErrorException in Model.php line 838:
Class '' not found
I'm assuming that has to do with the accessor is being processed before the the polymorphic relationship but I'm not sure.
I can simply use the following in my controller:
foreach (Gallery::with('galleryable')->get() as &$gallery) {
$gallery->galleryable_type = str_replace('app\\', '', strtolower($gallery->galleryable_type ));
}
But that seems like a dodgy way of doing things. Could a Laravel guru shed some light on the best way to tackle this problem?
Thanks!
Well I've found an interesting way to solve this issue.
In your models (App\Video and App\Image) you have to add:
protected $morphClass = 'video'; // 'image' for image class
then in your register method in service provider class add:
$aliasLoader = \Illuminate\Foundation\AliasLoader::getInstance();
$aliasLoader->alias('video', \App\Video::class);
$aliasLoader->alias('image', \App\Image::class);
This will cause that you will write image, and video in galleryable_type in the database instead of class names.
So now you can easily get to this values with:
echo $model->galleryable_type;

Model fields not recognized from the Intellisense

So, in the model I've listed the fillable and hidden fields, and then when I access fields on the object of that model, they get highlighted as Field 'some_field' not found in class .... If I add phpDoc to it as follows:
/** #var Trip $trip */
$trip->driver = ...
the field is being highlighted. If I write
/** #var object $trip */
$trip->driver = ...
it's not highlighted, but that's just not right. Everything works fine, but it just looks bad in the IDE, and the highlighting is annoying.
Then I decided to simply declare variables in the model class, for every field, so that they're recognized, but then the fields always hold NULL, when I access them on the object.
Anyone has a solution for that?
If you use PhpStorm you can write it above your model class as a comment.
/**
* App\User
*
* #property string $username
*/
You can also use a composer package called laravel-ide-helper here you have the link
https://packagist.org/packages/barryvdh/laravel-ide-helper
You can use some simple commands to generate helper files and it will provide you with code completion.

Yii2: Eagerly selecting calculated column and loading value into model-property

I thought I know every aspect of Yii2 in the meantime, but this one gives me headaches.
Situation
Two tables: Client and Billings. The Client-Table holds a regular list of clients. The Billing-table has several entries for each client (1:n).
Problem
I want to fetch a calculated DB-Field together with the row itself and access it via a virtual property of the model.
Key is that it gets calculated and selected together with the row itself. I know I can achieve something similliar with a regular virtual getter calculating the amount...but this is not at the same time as the select itself.
My Plan
In the query-object of the client-model i tried to add an an additional select (addSelect-Method) and give the field an alias. Then I added the alias of this select with the attributes-method of the model. Somehow this didn't work.
My Question
Does someone of you know the right way to achieve this? As this is a very common problem, I can not imagine this beeing too hard. I just somehow can't find the solution.
Sample code:
echo $client->sumOfBillings should output the contents of the corresponding property within the client-model. The contents of this property should be filled when fetching the client-row itself and not at the moment the property gets called.
I actual found the answer myself. Here is how you do it:
Query object
The fetching of all the Yii2-Models is done via their corresponding Query-Object. This object is retrieved via the models find()-Method. If you override this method, you can return your own query-object for that class. In the example above my model looks like this:
class Client extends \yii\db\ActiveRecord
{
//...
public static function find()
{
return new ClientQuery(get_called_class());
}
//...
}
Now within the Query-Objects init()-Method we can add the corresponding additional selects:
public class ClientQuery extends \yii\db\ActiveQuery
{
public function init()
{
parent::init();
//prepare subquery for calculation
$sub = (new Query())
->select('SUM(billing_amount)')
->from('billing')
->where('billing.client_id = client.id');
$this->addSelect(['client.*', 'sumBillings'=>$sub]);
}
}
We are now done with the query-Object. What have we done now? When selecting a client the sum gets calculated and loaded as well. But how do we access it? This was the hard part where I struggeled. The solution lies within the ActiveRecord-class.
Possibilities to populate the model with calculated data
There are several possibilities to load this data into the model-class. To understand what options we have, we can check out the populateRecord($record, $row)-method of the BaseActiveRecord-class:
/**
* Populates an active record object using a row of data from the database/storage.
*
* This is an internal method meant to be called to create active record objects after
* fetching data from the database. It is mainly used by [[ActiveQuery]] to populate
* the query results into active records.
*
* When calling this method manually you should call [[afterFind()]] on the created
* record to trigger the [[EVENT_AFTER_FIND|afterFind Event]].
*
* #param BaseActiveRecord $record the record to be populated. In most cases this will be an instance
* created by [[instantiate()]] beforehand.
* #param array $row attribute values (name => value)
*/
public static function populateRecord($record, $row)
{
$columns = array_flip($record->attributes());
foreach ($row as $name => $value) {
if (isset($columns[$name])) {
$record->_attributes[$name] = $value;
} elseif ($record->canSetProperty($name)) {
$record->$name = $value;
}
}
$record->_oldAttributes = $record->_attributes;
}
As you can see, the method takes the raw-data ($row) and populates the model instance ($record). If the model has either a property or a setter-method with the same name as the calculated field, it will be populated with data.
Final code of Client-Model
This is my final code of the Client-model:
class Client extends \yii\db\ActiveRecord
{
private $_sumBillings;
//...
public static function find()
{
return new ClientQuery(get_called_class());
}
public function getSumBillings()
{
return $this->_sumBillings;
}
protected function setSumBillings($val)
{
$this->_sumBillings = $val;
}
//...
}
The populateRecord()-method will find the setter-method ($record->canSetProperty($name)) and call it to fill in the calculated value. As it is protected, it is otherwise readonly.
VoilĂ ...not that hard actually and definitely useful!

Resources