I see that there's a 'last changed'-property in my table created with the extension builder called 'tstamp' but I can't figure out how to display it in the front-end.
In the front-end I'm using fluid like this, but it stays empty:
<f:format.date format="d.m.Y - H:i">{appointment.tstamp}</f:format.date>
I can see the property in the TCA also:
'ctrl' => array(
'title' => 'LLL:EXT:extTest/Resources/Private/Language/locallang_db.xlf:tx_extTest_domain_model_appointment',
'label' => 'start_date',
'tstamp' => 'tstamp',
I tried adding this in my php class file but it didn't change anything
/**
* #var DateTime
*/
protected $tstamp;
/**
* Get Tstamp
*
* #return DateTime
*/
public function getTstamp() {
return $this->tstamp;
}
I think the problem is that I don't understand the connection from the TCA to the PHP Class, can someone help?
The issue is that you also need a TCA configuration for every field as extbase gets there required information (especially for relations). Therefore add something like this into the TCA of your table:
'tstamp' => [
'label' => 'tstamp',
'config' => [
'type' => 'passthrough',
]
],
You don't need to add the field to a actual type.
The annotation in the model should be
/**
* #var \DateTime
*/
protected $tstamp;
So don't forget the \before the DateTime.
Clear caches and you are fine
Related
I'm trying the 4.1 new feature "Inline create", but I can't seem to associate the ids of the items created. Let me explain what I'm doing / what I want:
I have "Folders" that have "Chapters" inside (so 1-n relation).
My code:
CRUD::addField([ //Folder crud
'name' => 'chapters',
'type' => 'relationship',
'label' => 'Unidad',
'model' => "App\Models\Chapter",
'inline_create' => [
'entity' => 'chapter',
'modal_class' => 'modal-dialog modal-xl',
'modal_route' => route('chapter-inline-create'),
'create_route' => route('chapter-inline-create-save'),
]
]);
protected function setupCreateOperation() //Chapter crud
{
CRUD::setValidation(ChapterRequest::class);
CRUD::addField([
'name' => 'name',
'type' => 'text',
'label' => 'Nombre'
]);
}
public function chapters() //Folder model
{
return $this->hasMany(Chapter::class);
}
public function folder() //Chapter model
{
return $this->belongsTo(Folder::class);
}
It creates the main item and the related items no problem, but it doesn't actually relate them in the database at any point.
Any clue of what I might be doing wrong? Followed the docs but can't seem to make it work.
Thank you.
Do you have the right column names in the db ? The columns that are making the relationship possible, i.e in the folder table you should have a column named something like chapter_name or chapter_id, to identify the chapter where the folder belongs to.
Moreover, if those columns do not follow laravel conventions you need to add them as the second and third parameter when you are implementing the relationship in the models
More details here https://laravel.com/docs/8.x/eloquent-relationships#one-to-many
One note on this... I was running into this issue and realized that I forgot to make the parent_id fillable on my child model.
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'parent_id',
]
I'm trying to run a faker factory for relationships, but the field always returns NULL. How do fake a model relationship without hitting the database?
I have a Map factory with a one-to-one relationship to a parent Event table. I need to fake this relationship for unit testing:
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'event' => 'faker.' . join('_', $this->faker->words),
'category' => $this->faker->word,
'sub_category' => $this->faker->word,
'priority' => $this->faker->randomElement(['normal', 'high']),
'event' => Event::factory()->makeOne(),
];
}
This returns a fake model, but event is null, from the debugger:
result = {array} [5]
event = "faker.eum_voluptatibus_aut"
category = "libero"
sub_category = "aut"
priority = "high"
event = null
I tried using states, but the same thing happens:
public function disabled()
{
return $this->state([
'event' => Event::factory()->makeOne(['enabled' => false]),
]);
}
The object is returned with an empty event value. I need a faker object I can transverse down into: if ($object->event->enabled) [...]. How do I generate fake model relationships?
If you are using Laravel 8.x you must consider using methods used on the docs, it must look like that :
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'event' => 'faker.' . join('_', $this->faker->words),
'category' => $this->faker->word,
'sub_category' => $this->faker->word,
'priority' => $this->faker->randomElement(['normal', 'high']),
'event_id' => Event::factory(),
];
}
/**
* Indicate that the map is disabled.
*
* #return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function disabled()
{
return $this->state([
'event_id' => Event::factory()->create(['enabled' => false]),
]);
}
The only solution I found, so far, is to manually set the event key myself in my tests. It's not the ideal or elegant solution.
$fieldMap = Map::factory()->makeOne();
$fieldMap->event = Event::factory(['enabled' => false])->makeOne();
I don't like this approach. Why can't I define factories within factories?
I'm using the package searchable by nicolaslopezj https://github.com/nicolaslopezj/searchable and I'm trying to do a relation of belongs to, however I'm having a bit of trouble with it.
protected $searchable = [
/**
* Columns and their priority in search results.
* Columns with higher values are more important.
* Columns with equal values have equal importance.
*
* #var array
*/
'columns' => [
'products.title' => 10,
'products.description' => 5,
],
'joins' => [
"brand" => ['products.brand_id', 'brands.id']
],
];
public function brand()
{
return $this->belongsTo(Brand::class);
}
The problem here, is that it tries to get the database table 'brand' when its called 'brands'. However if I change this to brands I get a relationship error with laravel. So I'm really unsure what to do. The documentation also doesn't explain it very well. Would love some help, thanks!
The solution to your original problem as discussed with consideration of what was discussed in the comments, is to update your Brand model, by setting the protected $table variable to 'brand' and then adjusting the above to reflect that.
This should resolve your original problem of the relationship error and keep your code consistent as you've mentioned in the comments.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Brand extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'brand';
}
By default, Laravel will always assume that the database table is the plural form of the model name. And so, if you model is singular, you should assume it expects the database table to be the plural form unless explicitly set as above!
Adjusting your other code as follows, to reference the brand.id rather than brands.id:
protected $searchable = [
/**
* Columns and their priority in search results.
* Columns with higher values are more important.
* Columns with equal values have equal importance.
*
* #var array
*/
'columns' => [
'products.title' => 10,
'products.description' => 5,
],
'joins' => [
"brand" => ['products.brand_id', 'brand.id']
],
];
public function brand()
{
return $this->belongsTo(Brand::class);
}
I have written like this
/**
* itemRepository
*
* #var \KRT\KrtEmployee\Domain\Repository\ItemRepository
* #inject
*/
protected $itemRepository = null;
/**
* action list
*
* #return void
*/
public function listAction()
{
$arguments =$this->request->getArguments();
$employees = $this->itemRepository->findAll();
$this->view->assign('employees',$employees);
}
In my $employees result I have
Employee ID (Default uid)
Name
Designation
Department
Salary
Now, How can I Sort the result array in ascending order based on
Name
Department and Salary
Is there any default function to sort inside the repository queries?
Every repository has a $defaultOrderings property where you can specify the default orderings applied to all query methods. In your case it could look like this:
protected $defaultOrderings = [
'name' => QueryInterface::ORDER_ASCENDING,
'department.name' => QueryInterface::ORDER_ASCENDING,
'salary' => QueryInterface::ORDER_ASCENDING,
];
As you can see with department.name you can also sort by properties of relations. Notice that this only works for 1:1 and n:1 relations.
In case of custom query methods in your repository you can manually set the orderings directly on the query:
$query->setOrderings([
'name' => QueryInterface::ORDER_ASCENDING,
'department.name' => QueryInterface::ORDER_ASCENDING,
'salary' => QueryInterface::ORDER_ASCENDING,
]);
You have multiple options depending on what you would like to achieve:
Setting a default order for the entire repository
Add the following to your repository class
protected $defaultOrderings =
array(
'department' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
'salary' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
);
This will apply to all queries made in this repository.
see: https://wiki.typo3.org/Default_Orderings_and_Query_Settings_in_Repository
Setting an order for a single query
Add this to a query you define in your repository
$query->setOrderings(
array(
'department' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
'salary' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
)
);
In this way you could (and would have to) implement a different access method for each sort order you would like to have returned.
see: https://docs.typo3.org/typo3cms/ExtbaseFluidBook/6-Persistence/3-implement-individual-database-queries.html
Sort the result
You can always use PHP sorting methods to sort the query result (possibly converting it to an array with ->toArray() first.
In general and complete:
<?php
namespace <yourVendor>\<yourExtensionkey>\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
class <yourDomain>Repository extends \TYPO3\CMS\Extbase\Persistence\Repository {
protected $defaultOrderings = [
'<yourProperty1>' => QueryInterface::ORDER_ASCENDING,
'<yourProperty2>' => QueryInterface::ORDER_DESCENDING
];
}
Further explanation in docs.typo3.org
I have a zf2 application that works with doctrine.
I have the following entity:
class Role
{
/**
* #var int
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(type="string", length=255, unique=true, nullable=true)
*/
protected $name;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="YrmUser\Entity\Role", mappedBy="parent")
*/
protected $children;
/**
* #var Role
* #ORM\ManyToOne(targetEntity="YrmUser\Entity\Role", inversedBy="children", cascade={"persist"})
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
protected $parent;
}
for this entity i have a form:
class RoleForm extends Form
{
/**
* [init description]
*
* #return void
*/
public function init()
{
$this->setHydrator(
new DoctrineHydrator($this->objectManager, 'YrmUser\Entity\Role')
)->setObject(new Role());
$this->setAttribute('method', 'post');
$this->add(
array(
'name' => 'name',
'attributes' => array(
'type' => 'text',
'placeholder' =>'Name',
),
'options' => array(
'label' => 'Name',
),
)
);
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'parent',
'attributes' => array(
'id' => 'parent_id',
),
'options' => array(
'label' => 'Parent',
'object_manager' => $this->objectManager,
'property' => 'name',
'is_method' => true,
'empty_option' => '-- none --',
'target_class' => 'YrmUser\Entity\Role',
'is_method' => true,
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('parent' => null),
),
),
),
)
);
}
}
The hydration for the select in the form works as it only shows other roles that don't have a parent.
But when editing a existing entity it shows itself in the select so i can select itself as its parent.
I figured if i would have the id of current entity inside the form i can create a custom repo with a method that retrieves all roles without a parent and does not have the current entity id.
But i cant figure out how to get the id of the currently edited entity from inside the form.
Any help is appreciated.
Cheers,
Yrm
You can fetch the bound entity within the form using $this->getObject().
You have actually already set this with setObject(new Role());. Unfortunately this means that it was not loaded via Doctine and you will have the same issue, no $id to work with.
Therefore you will need to add the 'parent role' options (value_options) after you have bound the role loaded via doctrine.
From within the controller, I normally request the 'edit' form from a service class and pass in the entity instance or id that is being edited. Once set you can then modify existing form elements before passing it back to the controller.
// Controller
class RoleController
{
public function editAction()
{
$id = $this->params('id'); // assumed id passed as param
$service = $this->getRoleService();
$form = $service->getRoleEditForm($id); // Pass the id into the getter
// rest of the controller...
}
}
By passing in the $id when you fetch the form you can then, within a service, modify the form elements for that specific role.
class RoleService implements ObjectManagerAwareInterface, ServiceLocatorAwareInterface
{
protected function loadParentRolesWithoutThisRole(Role $role);
public function getRoleEditForm($id)
{
$form = $this->getServiceLocator()->get('Role\Form\RoleEditForm');
if ($id) {
$role = $this->getObjectManager()->find('Role', $id);
$form->bind($role); // calls $form->setObject() internally
// Now the correct entity is attached to the form
// Load the roles excluding the current
$roles = $this->loadParentRolesWithoutThisRole($role);
// Find the parent select element and set the options
$form->get('parent')->setValueOptions($roles);
}
// Pass form back to the controller
return $form;
}
}
By loading the options after the form has initialized you do not need the current DoctrineModule\Form\Element\ObjectSelect. A normal Select element that has no default value_options defined should be fine.