How would I change entity labels? - sonata-admin

I use Symfony4 and Sonata admin. When I use ModelListType it worked as shown on the screenshot below.
How would I change entity item machine name: ('App\Entity\Product:000000003aaca7040000000026c8b335') to entity item field 'name' value?
My code for this field is:
#/project/src/Admin/ProductAdmin.php
...
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('category', ModelListType::class);
}
...

I have solve this by adding __toString() method to entity:
public function __toString(){
return $this->getName();
}

Just return the value directly by name instead of calling the getter method like,
public function __toString()
{
return $this->name;
}

Related

Laravel - Display Array/String Object in view from database

Data are stored as ["item_1", "item_2"] in database like shown below.
I want to display those data in view blade properly.
Product Model
protected $fillable = ['name', 'prod_id'];
public function models() {
return $this->hasMany(Model::class, 'prod_id');
}
Model Model
protected $fillable = ['model', 'prod_id'];
protected $cat = ['model'=>'array'];
public function model()
{
return $this->belongsTo(Product::class, 'prod_id');
}
Controller - store method
public function create (Request $request, Product $product){
$models = new Model;
{
$model = json_encode(request('models'));
$items->models = $model;
$product->models()->save($models);
}
}
Controller show method
public function show(request $id){
$product = Product::findorfail($id);
$models = Model::with(['model'])->where('prod_id', $product->id)->get();
return view ('show', compact('product', 'models'));
Create View
<input type="checkbox" name="model[]" value="Samsung">
<input type="checkbox" name="model[]" value="Nokia">
<input type="checkbox" name="model[]" value="Apple">
<button>Add Model</button>
I tried show view:
#foreach($models as $model)
{{ json_decode($model->models) }}
#endforeach
It throws
htmlspecialchars() expects parameter 1 to be string, array given
What am I missing.
PS: MySQL does not support json column, so I saved as text column.
you need to do someting like this.
Model Model
protected $fillable = ['models', 'prod_id']; // screenshot says that the field name is "models"
protected $cast = ['models' => 'array']; // the property is $cast no $cat
public function product()
{
return $this->belongsTo(Product::class, 'prod_id');
}
ModelController - store method
public function store (Request $request){
$product = Model::create([
'models' => json_encode($request->models),
'prod_id' => $request->prod_id
]);
return redirect()->back()->with('success', 'created!');
}
public function show(Request $id){
$model = Model::findOrFail($id)->with('product');
return view ('model.show', compact('model'));
}
ProductController show method
public function show(request $id){
$product = Product::findOrFail($id)->with('models'); // the method name is findOrFail() no findorfail
// $models = Model::with(['model'])->where('prod_id', $product->id)->get();
return view ('show', compact('product'));
}
Into the show View
#foreach($product->models as $models)
#foreach(json_decode($models->models) as $model)
{{ $model }}
#endforeach
#endforeach
Your Models Model confuses me a little bit. You seem to have a field name model that's the same as a relationship method name. That means whenever you include that relation, it'd functionally override that property with data from the related table. (I say 'functionally' because you're using dynamic properties, whereas it is actually possible to explicitly tell Eloquent whether you want an attribute or relation without making it guess.)
That said, your $model->models property could be coming back as an array for one of two reasons. The first is that it may be accidentally referring to a relational data-set and not the JSON string you were expecting. The second is you've corrected the protected $cat = ['model'=>'array']; to read protected $cast = ['models'=>'array'];, and it's stepping on your toes now. By casting it to an array, it may be getting automatically get interpreted back into one before you call json_decode on it.
Either way, I'd dd($model->models) to see what it is first.
You need to change your foreach like this:
#foreach($models->models as $model)
{{ json_decode($model) }}
#endforeach
because Your array is like this
{"id":18,"prod_id":22,"models":{"id":22,"user_id":1}}
In here the $models is getting only id and prod_id models is still array so your foreach should be #foreach($models->models as $model)
Sample Code is here:
$arr = '{"id":18,"prod_id":22,"models":{"id":22,"user_id":1}}';
echo '<pre>';
foreach (json_decode($arr->models) as $str){
echo $str;
}

Make a list field editable when this field is a many_to_one type using in Sonata-project Symfony

My entity
/**
* #ORM\ManyToOne(targetEntity="Estat", inversedBy="temes")
*/
private $estat;
public function setEstat(\Ncd\ForumBundle\Entity\Estat $estat = null)
{
$this->estat = $estat;
return $this;
}
My admin
protected function configureListFields(ListMapper $listMapper)
{
//$estats=$this->getEstatsPossibles()->toArray();
$estats=array();
foreach($this->getEstatsPossibles() as $estat)
{
$estats[$estat->getId()]=$estat->getNom();
}
$listMapper
->add('estat', 'choice',['editable' => true,'choices'=> $estats])
I'd like to make estat field editable in the list grid. Doing it on this way I get make it editable, a combobox appears but when I chose an option I get an exception because setEstat function of my entity does not recive an Estat entity, but a string (the array's key).
Trying
->add('estat', 'many_to_one',['editable' => true,'choices'=> $estats])
Only appears a link to the entity without any possibility to change.
Is it possible?
Waiting for a better and cleaner solution I'v solved this injecting an entityManager in my entity following the solution of this answer:
Get entityManager inside an Entity
Then, in my entity I've changed setEstat function:
public function setEstat( $estat = null)
{
if (is_object($estat) && get_class($estat)=='Ncd\ForumBundle\Entity\Estat')
{
$this->estat=$estat;
} else {
$estat_o=$this->em->getRepository('Ncd\ForumBundle\Entity\Estat')->find((int)$estat);
$this->estat = $estat_o;
}
return $this;
}

Sonata Admin Bundle: show total count of collection on list view

Is there any way to show total count of collection on list view? Imagine that there is a user that can have many links. How can I show total links count on list view?
Show field it is quite easy, there is solution for sorting by this virtual field.
Entity/Some.php more about count here Extra Lazy Associations
public function getCommentsCount()
{
return $this->getComments()->count();
}
SomeAdmin.php override createQuery and configure list field
public function createQuery($context = 'list')
{
$query = parent::createQuery($context);
if ('list' === $context) {
$rootAlias = $query->getRootAliases()[0];
//...
$parameters = $this->getFilterParameters();
if ('getCommentsCount' === $parameters['_sort_by']) {
$query
->leftJoin($rootAlias.'. comments', 'cm')
->groupBy($rootAlias.'.id')
->orderBy('COUNT(cm.id)', $parameters['_sort_order'])
;
}
//...
}
return $query;
}
/**
* #param ListMapper $listMapper
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('id')
//...
->add(
'getCommentsCount',
null,
[
'sortable' => true,
'sort_field_mapping' => ['fieldName' => 'id'],
'sort_parent_association_mappings' => [],
]
)
//....
}
service.yaml add "simple" paginator (count does not work correctly)
tags:
- { name: sonata.admin, pager_type: "simple", ...
Reasons:
subquery in orm join is not allowed
subquery in orm orderBy is not allowed
HIDDEN field does not work
\Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery::getFixedQueryBuilder
(// for SELECT DISTINCT, ORDER BY expressions must appear in idxSelect
list)
My answer is similar to Khalid (above) but has some key differences.
If you wrap the collection in a count( $entity->getLinks() ) then this will issue a query which returns every link association.
The downside of this is that if you have 1000s of Links associated, the memory resources required will need to be sufficient for hydrate each entity. (Which can be huge if you have thousands of different entities).
Instead, you should mark your Entities as EXTRA_LAZY and then use the --$entity->getLinks()->count()` method which will not do any hydration, instead it will only issue the COUNT queries.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
So do the following:
/**
* #ManyToMany(targetEntity="Links", mappedBy="whatever", fetch="EXTRA_LAZY")
*/
public $links;
Then you can call:
public function getTotalLinks(){
return $this->getLinks()->count();
}
And it will be super quick.
with Sonata 3.**
in somwhere in Admin***.php script for listing all fields:
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
//...
->addIdentifier('getCommentsCount', IntegerType::class, [ 'label' => '#Comments'])
;
}
Where in Entity i written something like this:
public function getCommentsCount()
{
return $this->comments->count();
}
that works for me )
Yes you can show the total count of links for each user, i assume you have arraycollection of links defined in your user entity, define a property named as $totalLinks and in getter of that property return count of links something like below
class User{
public $totalLinks;
public function getTotalLinks(){
return count($this->getLinks());
}
}
and then in your configureListFields() you can add $totalLinks property
protected function configureListFields(ListMapper $list)
{
$list
->add('...')
->add('...')
->add('totalLinks');
}
Found answer here:
SonataAdminBundle custom rendering of text fields in list
I'm using Sonata 2.3 so TWIG template should be like:
{% extends admin.getTemplate('base_list_field') %}
{% block field %}
{{ value|length }}
{% endblock %}

Can't read named parameter in controller CakePHP

I'm trying to pass a named parameter to a function. It actually is passed through before $this->request->is('post'), but debugKit placed after this line returns null. What gives?
Route:
http://localhost/bake/users/login/ref:post
Controller:
public function login() {
//it returns 'post' here successfully.
debug($this->params['named']['ref']);
if ($this->request->is('post')) {
//it returns 'null' here.
debug($this->params['named']['ref']);
}
}
I used sort of a pseudo method to go about it:
public function login() {
//set the value to the view.
$this->set('param', $this->params['named']['ref']);
if ($this->request->is('post')) {
$param = $this->request->data['param'];
}
}
In the view I added a hidden field:
<input type="hidden" name="data[param]" value="post"/>
So it gets the value with form is submitted.
If you're lazy:
$this->request->query('ref')
If you're curious:
Documentation here
Source code here

Custom accessors Eloquent Model

I have a Eloquent Model and I want to create a customized toArray method...
class Posts extends Model {
public function scopeActives($query)
{
return $query->where('status', '=', '1');
}
public function toCustomJS()
{
$array = parent::ToArray();
$array['location'] = someFunction($this->attributes->location);
return $array;
}
}
//In my controller:
Posts::actives()->get()->toArray(); //this is working
Posts::actives()->get()->toCustomJS(); //Call to undefined method Illuminate\Database\Eloquent\Collection::toCustomJS()
How can I override the toArray method or create another "export" method?
get() actually returns a Collection object which contains 0, 1, or many models which you can iterate through so it's no wonder why adding these functions to your model are not working. What you will need to do to get this working is to create your custom Collection class, override the toArray() function, and also override the function in your model responsible for building that collection so it can return the custom Collection object.
CustomCollection class
class CustomCollection extends Illuminate\Database\Eloquent\Collection {
protected $location;
public function __construct(array $models = Array(), $location)
{
parent::__construct($models);
$this->location = $location;
}
// Override the toArray method
public function toArray($location = null)
{
$original_array = parent::toArray();
if(!is_null($location)) {
$original_array['location'] = someFunction($this->location);
}
return $original_array;
}
}
Overriding the newCollection method on your models
And for the models you wish to return CustomCollection
class YourModel extends Eloquent {
// Override the newCollection method
public function newCollection(array $models = Array())
{
return new \CustomCollection($models, $this->attributes['location']);
}
}
Please note this may not be what you are intending. Because a Collection is really just an array of models, it's not good to depend on the location attribute of a single model. Depending on your use-case, it's something that can change from model to model.
It might also be a good idea to drop this method into a trait and then just use that trait in each model you wish to implement this feature in.
Edit:
If you don't want to go through creating a custom Collection class, you can always just do it manually each time...
$some_array = Posts::actives()->get()->toArray();
$some_array['location'] = someFunction(Posts::first()->location);
return Response::json($some_array);

Resources