How to get an array of fe_groups in TYPO3? - php

I'm pretty new to TYPO3 and many things are confusing at the moment, especially how the data modeling and data fetching actually works if you're relying on ExtBase.
Thing I want to achive is to get an array of records from the fe_groups table and pass it into my Fluid view and render those items in f:form.select input field.
So far, I've tried nothing since I have no idea from where and how to start it.
Other thing I've did successfully is to pass a hard coded array of object items into my view, and rendered them successfully, like this:
<f:form.select
class="form-control"
property="taskTypes"
options="{taskTypes}"
optionValueField="name"
optionLabelField="value"
id="taskTypes" />
This is the method in my Controller which fills the taskTypes array:
private function getTaskTypes() {
$task_type_names = [
' - Task Types - ',
'New client',
'Maintenance',
];
$task_types = [];
foreach($task_type_names as $i => $task_type_name) {
$task_type = new \stdClass();
$task_type->key = $i;
$task_type->value = $task_type_name;
$task_types[] = $task_type;
}
return $task_types;
}
And then a simple view assignment in controller's action:
$this->view->assign('taskTypes', $this->getTaskTypes());
And this works like a charm!
But I'm clueless how to do something similar with dynamic content fetched from the database tables.
So, basically, I just need a way to pass items from fe_groups table to my view and render them.

You'll have to inject the Repository for FrontenduserGroups from Extbase
/**
* #var \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserGroupRepository
* #inject
*/
protected $feUserGroupRepository;
in your method you can then use this Repository to get the data from the database
$feUserGroup = $this->feUserGroupRepository->findAll();
$userByUid = $this->feUserGroupRepository->findByUid(12);
The repository also provides more ->findBy* methods.
Here is a cheatsheet that might help you http://lbrmedia.net/codebase/Eintrag/extbase-query-methods/
Note:
the #inject in the doc comment is actually parsed by Extbase and loads the class that is refered in #var
the storagePid needs to be set to the UID of the folder that contains the usergroups in the backend

Related

Laravel moving Controller logic to a Model

I'm at a stage where I'm refactoring my code, and I've come across an interesting conundrum.
In my ArticleController I have a bog standard store method for storing an article in my articles database table.
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(StoreArticle $request)
{
$article = new Article();
$defauultPublished = "draft";
$IntranetOnly = false;
$isFeatured = false;
$isFeatured = ($request->get('featuredArticle') == "1" ? true : false);
$IntranetOnly = ($request->get('IntranetOnly') == "1" ? true : false);
$article->title = $request->get('title');
$article->slug = str_slug($request->get('title'));
$article->author = $request->get('author');
$article->category = $request->get('category');
$article->excerpt = $request->get('excerpt');
$article->content = clean($request->get('content'));
$article->featuredImage = $request->get('featuredImage');
$article->featuredVideo = $request->get('featuredVideo');
$article->readingTime = $this->calculateReadTime($request);
$article->featuredArticle = $isFeatured;
$article->IntranetOnly = $IntranetOnly;
$article->published = $defauultPublished;
$article->save();
$article->handleTags($request);
return redirect('editable/news-and-updates')->with('success', 'Article has been added');
}
I also have a function for calculating read time:
/**
* Calculate a rough reading time for an articles by counting the words present
* These words are then divided by a given reading time and rounded to the nearest whole number
* Reading time average is roughly 267 words per minute, so this also accounts for relatively slow readers
*
* #param Request $request
* #return void
*/
public function calculateReadTime(Request $request)
{
$readingSpeed = 200;
$title = str_word_count(strip_tags($request->get('title')));
$excerpt = str_word_count(strip_tags($request->get('excerpt')));
$content = str_word_count(strip_tags($request->get('content')));
$words = ($title + $excerpt + $content);
$minutes = round($words / $readingSpeed);
return $minutes . ' minute' . ($minutes == 1 ? '' : 's');
}
My question is should these methods be moved to the Article model?
Controller should be as slim as possible. Following a resourceful approach (which you seem to be doing), the store() method in your ArticleController class should strive as much as possible to look like this:
class ArticleController extends Controller
{
public function store(CreateArticleRequest $request)
{
$article = Article::create($request->validated());
// Redirect with success message
}
}
Here, your request data is validated in a form request class before it even reaches the controller method; and then an Article model instance is created from that validated data.
A couple of other notes…
Statements like ($data['featuredArticle'] == "1" ? true : false) are overly verbose. You’re doing a condition check which will evaluate to true or false; you don’t need to manually return each value in a ternary operator. So this could be slimmed down to $data['featuedArticle'] == '1'. Furthermore, if you pass a value of 0 by default, then you could just get rid of the check entirely. If in your Blade template you put a hidden input before your checkbox:
<input type="hidden" name="featuredArticle" value="0" />
<input type="checkbox" name="featuredArticle" value="1" />
Then 1 will be send if the checkbox is checked (as it overrides the hidden input’s value, or 0 sent if the checkbox isn’t checked).
Also, try to stick to Laravel conventions to make your life easier. If you use snake_case for your input names, then it just makes life easier matching them up to model attribute and table column names. So use featured_article, have an attribute in your model with the same name, which maps to a database column with the same name again. This allows you to do shorthand calls like create() (as per my controller example) and update().
Finally, methods like calculating reading time definitely belong on your model. Models represent something in your application. It therefore follows that you can do things with your models. Calculating the time to read an Article model instance therefore lends itself to having a calculateReadingTime() method on the Article model.
A bit long-winded, but hopefully there should be some helpful pointers for you in the above. I’ve been working on Laravel projects for around five years now and have found that this approach and conventions is what works best.
Your controller's store article is fine, because it fills your article instance based on request data. It could use some refactoring and you could encapsulate more logic into your Article (for example, assign slug field inside your Article model whenever title is changed and so on).
But the line $article->handleTags($request); is a suspect, because your model should never operate with requests - it will quickly polute your model code with very specialized dependencies that you don't want (what happens when you receive your tags from cache and don't have a request instance? What happens if other type of request contains tags differently? and so on). Your model shouldn't have knowledge about requests or other parts of your app. Your controller is connecting the dots between them, so make sure your handleTags takes some basic abstract types/structures as a parameter (for example, an array) and make sure your controller takes and transforms data from request accordingly before feeding it to your article.
As for your calculateReadTime dilemma, it should definitely be inside your model. Think about it this way - do you have everything you need to calculate read time of your article inside your Article model? The answer is yes, it's a property of an article object, doesn't matter if you store it in DB or calculate it off other properties. Make getReadTime method. You don't want a controller to compute something about your model because it will tie that logic to a specific place in your app which is bad (what happens when you need to calculate read time of an article in other controller? Other model? and so on).
Make sure you read about has and is concepts regarding object-oriented design, it will help you immensely.
I think you should move those assignments to a Service Class. You could also go ahead and create a repository class. This would thus become your code structure:
Controller -> Service -> Repository -> Model.
Doing this $article = new Article(); is bad. You will have a had time when writing a test for your controller store method.
I would suggest you do this:
Create a Service class, say ArticleService.php. Define a store method in it.
ArticleService.php
use Article;
class ArticleService {
protected $article;
public function __construct(Article $article){
$this->article = $article;
}
public function store(array $data){
$defauultPublished = "draft";
$IntranetOnly = false;
$isFeatured = false;
$isFeatured = ($data['featuredArticle'] == "1" ? true : false);
$IntranetOnly = ($data['IntranetOnly'] == "1" ? true : false);
$this->article->title = $data['title'];
$this->article->slug = str_slug($data['title']);
$this->article->author = $data['author'];
$this->article->category = $data['category'];
$this->article->excerpt = $data['excerpt'];
$this->article->content = clean($data['content']);
$this->article->featuredImage = $data['featuredImage'];
$this->article->featuredVideo = $data['featuredVideo'];
$this->article->readingTime = $data['reading_time'];
$this->article->featuredArticle = $isFeatured;
//Capital letter I? You should be consistent with your naming convention
$this->article->IntranetOnly = $IntranetOnly;
$this->article->published = $defauultPublished;
if($this->article->save()){
$this->article->handleTags($request);
return true;
}
return false;
}
}
And your Controller now becomes:
class ArticleController{
protected $articleService;
public function __construct(ArticleService $articleService){
$this->articleService = $articleService;
}
public function store(Request $request){
//Some Validation Logic
$readingTime = $this->calculateReadTime($request)
$data = array_merge(['reading_time' => $readTime], $request->all());
return $this->articleService->store($request->all());
}
}
I also see that you are not validating the incoming Request. You should always do that because you can/should never trust your users to always provide/input the right data. It is your duty to force them to do that. e.g I as your user might decide to enter my name in your email field. If you don't validate that data, you will end up with wrong data.
There is also the issue of individually assigning your request parameter to their corresponding Model attribute. I decided to leave it that way so as not to overload you with information.
In summary, just take a look at the following resources for more insight.
https://laravel.com/docs/5.1/quickstart-intermediate
https://laravel.com/docs/5.6/validation
In short, read up the whole Laravel documentation! Goodluck!

How to generate SQL from Request?

Currently I use next approach for retreiving data according to request:
/**
* #QueryParam(name="filters", nullable=true, map=true, description="Filter by fields. Must be an array ie. &filters[id]=3")
*/
public function cgetAction(ParamFetcherInterface $paramFetcher)
{
$filters = $paramFetcher->get('filters') ?: [];
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository($this->entityClassName())
->findBy($filters);
return $entities;
}
But I need something like this: specify complex conditions in GET request, for example
?filter={"where":{"or":[{"id":1},{"id":2},...,{"id":20"},{"id":21}]}}
?filter[where][date][gt]=2014-04-01T18:30:00.000Z
?filter={"where": {"keywords": {"inq": ["foo", "bar"]}}}
?filter[where][and][0][title]=My%20Post&filter[where][and][1][content]=Hello
etc
and get data from repository in according to this request.
Does exist any bundle for Symfony for this purpose? Will be glad for any advice.
Use the LexikFormFilterBundle, it's made for this use case, building form filters and then build a doctrine query from this form filter.
You'll find a complete example here.

Creating a new Laravel 4 model class with a string variable

TL;DR is at the end to cut to the chase.
I have a lot of belongsTo() models that can have any number of records, and I'm trying to bind them to an edit form. I have the following foreach that creates the form elements:
#foreach ($department->department_10 as $key => $value)
{{ Form::select(
'department_10['.(isset($value->pk_department_10)?$value->pk_department_10:0).']',
$department_10_opts,
(isset($value->department_10)?$value->department_10:''),
array('class'=>'form-control input-md department_10', 'placeholder'=>'Other Types of Service')) }}
#endforeach
Since there can be 0 records (rows?) that belong to the model, to simplify my #foreach, I wanted to create a "blank" instance of the model. Additionally, because I'm going to have to deal with about 70 more cases like this, I created a function that would create the new blank model. Here's the function (in my controller for lack of a better place):
function mkBlankModel($parentModel, $newModel){
if(count($parentModel->$newModel) === 0){
$parentModel->$newModel[0] = new $newModel();
$parentModel->$newModel[0]->fk_department = $parentModel->pk_department;
$parentModel->$newModel[0]->$newModel = '';
}
return $parentModel;
}
When I run it, I don't get any errors, but I do get unexpected results and I can't really make sense of them:
Test Step 1) View the edit page while loading a record with 2 department_10's. It works as expected; loads two fields properly.
Test Step 2) View the edit page while loading a record with 0 department_10's. The page loads but without any fields. Because apparently my function didn't work, so I verify by dumping dump($department->$department_10) and it confirms this.
Test Step 3) I replace the $parentModel->$newModel[0] with $parentModel->department_10[0] like so:
function mkBlankModel($parentModel, $newModel){
if(count($parentModel->$newModel) === 0){
$parentModel->department_10[0] = new $newModel();
$parentModel->department_10[0]->fk_department = $parentModel->pk_department;
$parentModel->department_10[0]->$newModel = '';
}
return $parentModel;
}
And both scenarios (with records and without) work just fine. So my problem likely isn't Laravel specific, but I'm just curious how I can accomplish this.
TL;DR:
I'm trying to create a model instance for a parent model, if one doesn't exist, so a blank field will be created by my #foreach loop in my edit.blade.php's form. I can do this just fine if I manually spell out the model's name when creating it, but since I'll be doing this frequently, I'd prefer to define the class, and populate it with a string.
So I figured it out; I needed to wrap the $newModel in curly-brackets:
function mkBlankModel($parentModel, $newModel){
if(count($parentModel->$newModel) === 0){
$parentModel->{$newModel}[0] = new $newModel();
$parentModel->{$newModel}[0]->fk_department = $parentModel->pk_department;
$parentModel->{$newModel}[0]->$newModel = '';
}
return $parentModel;
}
More info here: http://php.net/manual/en/language.types.string.php#language.types.string.parsing.complex

append a related Model in Phalcon

I wrote a vcard class with Phalcon in PHP. The vCard Model is initialized like this.
// Inside the BS_VCard class
public function initialize(){
$this->hasMany("id","BS_VCardElement","vCardId",array(
"alias" => "elements",
'foreignKey' => array(
'action' => Phalcon\Mvc\Model\Relation::ACTION_CASCADE
)
));
}
Its elements are initialized like this
// Inside the BS_VCardElement class
public function initialize(){
$this->belongsTo("vCardId","BS_VCard","id",array("alias" => "vCard"));
...
}
If a user reads a vCard and adds another element, it doesn't work as expected. To simplify the use I added some fascade methods like this
public function addDateOfBirth($date){
$element = new BS_VCardElement();
$element->setName("BDAY");
$element->addValue($date);
// This doesn't work
$this->elements[] = $element;
}
The Docs/Storing related records do not explain how to append fresh data like this to the related table.
I also tried this
$this->elements[] = array_merge($this->elements,array($element));
But the save method seems to ignore the added element. Save() returns true.
This question has been asked a couple of months ago but since I ran into a similar issue I decided to share my results anyway.
And here's what I found. Lower case aliases ('elements') don't seem to work whereas upper case aliases ('Elements') do.
To add one element you can do this;
$this->Elements = $element;
To add multiple elements you can do this;
$elements = array($element1, $element2);
$this->Elements = $elements;
After that you have to save the vcard before accessing the elements again. If you don't, phalcon will just return a result set with only the elements already in the database. (Not sure if this can be changed somehow.)
And here's the documentation (where all this is not mentioned): http://docs.phalconphp.com/en/latest/reference/models.html#storing-related-records
According to the Phalcon source code, the Resultset object is immutible.
/**
* Resultsets cannot be changed. It has only been implemented to
* meet the definition of the ArrayAccess interface
*
* #param int index
* #param \Phalcon\Mvc\ModelInterface value
*/
public function offsetSet(var index, var value)
{
throw new Exception("Cursor is an immutable ArrayAccess object");
}
It appears that replacing the element with an array is the only way to implement an "append" or modification of the resultset (other than delete which IS supported).
Of course this breaks the \Phalcon\Mvc\Model::_preSaveRelatedRecords() because the function ignores the class properties and refetches the related from the Model Manager (and resets the model::$element attribute at the end).
I feel frustrated by this because appending objects to a collection seems like a very common task and not having a clear method in which to add new items to a parent seems like a design flaw.
I think related elements might have some magic functionality invoked when you set the properties, so simply using $this->elements[] (evidently) doesn't work. Perhaps try re-setting the entire variable:
public function addDateOfBirth($date){
$element = new BS_VCardElement();
$element->setName("BDAY");
$element->addValue($date);
$elements = $this->elements;
$elements[] = $element;
$this->elements = $elements;
}

Getting the fields attached to a bundle of an entity in Drupal

I don't want to get any information from the fields, all I want to get are the field machine names attached to a specific bundle (instance of an entity).
I'm looking into entityfieldquery, entity_load, and entity_get_info, and and I'm leaning towards entity_get_info, but now I'm reading that use is deprecated.
function multi_reg_bundle_select() {
$query = entity_get_info('registration');
}
How do I get information from the attached bundle? ('registration['bundlename']')? Ultimately I just want to get the fields attached to a particular bundle. Preferably in an array of strings.
You can find the answer at https://drupal.stackexchange.com/questions/14352/listing-entity-fields
Short answer: use
$fields = field_info_instances();
to get all info about all entity types and bundles, or use
$fields = field_info_instances('node', 'article');
to get only the fields of the node type "article".
The easiest way to get only the field machine names attached to a specific bundle would be this:
$field_names = array_keys(field_info_instances('node', 'article'));
Using the function already mentioned; a disadvantage of field_info_instances() in some circumstances is that it does not provide the field type. The lightest weight function for that in Drupal 7 is field_info_field_map(). It can be put in a helper function like this:
/**
* Helper function to return all fields of one type on one bundle.
*/
function fields_by_type_by_bundle($entity_type, $bundle, $type) {
$chosen_fields = array();
$fields = field_info_field_map();
foreach ($fields as $field => $info) {
if ($info['type'] == $type &&
in_array($entity_type, array_keys($info['bundles'])) &&
in_array($bundle, $info['bundles'][$entity_type]))
{
$chosen_fields[$field] = $field;
}
}
return $chosen_fields;
}
And use it like so, to get all taxonomy fields on the article content type:
$fields = fields_by_type_by_bundle('node', 'article', 'taxonomy_term_reference');
Note that field_info_field_map() gives only the machine name (as the original poster requested), but you'd have to load the field object with field_info_field() to get the field label (human-readable name).
I believe that field_info_bundles() may be what I am looking for. I'll let people know when I've tested it (but still, if you have suggestions, I'm happy to hear them!)
https://api.drupal.org/api/drupal/modules!field!field.info.inc/function/field_info_bundles/7

Categories